一、贪心算法概念
贪心算法通过做出一系列选择来求出问题的最优解,在每个决策点,它做出在当时看来最佳的选择。由此可见,贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择,但是对许多问题它都所得到的解都是全局最优解。
二、相关题目
第一题:Sicily 1198. Substring
题意:给n个字符串,需要将他们拼接起来形成一个字典序最小的字符串
解决思路:重写比较函数,然后利用sort() 函数进行排序。字符串传递性的证明:字符间的比较用ASCII码,而字符串之间的比较就类似于多位数的比较(把字符看成个位数,字符串看成多位数),数字的比较是有传递性的,同时字符串也满足传递性。
代码:
#include
#include
using namespace std;
//重写比较函数,用两种方式连接两字符串,按得到的字符串字典序较小的情况来排序
//如果不重写比较函数,那么就是直接按字典序比较 a,b 的大小
//但是这样排序后将字符串连接起来的不一定是最小字典序
//比如:b,ba; 按字典排序后再连接起来是 bba, 但正确应该是 bab
bool compare(string s1, string s2) {
string r1 = s1 + s2;
string r2 = s2 + s1;
return r1 < r2;
}
int main() {
int t;
cin >> t;
while(t--) {
int n;
cin >> n;
string str[8];
for(int i = 0; i < n; i++) cin >> str[i];
//按重写的比较函数进行排序,使得排序后得到的字符串是字典序最小的
sort(str, str+n, compare);
for(int i = 0; i < n; i++) cout << str[i];
cout << endl;
}
return 0;
}
第二题:Sicily 1681 Matchsticks
解决思路:先从数字2开始枚举,后面可以发现其中的规律。当火柴数目为偶数时,将所在数字都拼成数字1可得到最大的数字;当火柴数目为奇数时,可将落单的那根和另外两根拼成数字7并放在最高位,其余的均拼成1。对于最小的数字,先把前几个找出来,后面的可以根据前面的得出,因为要尽量拼成数字8(需要7根火柴),所以有:ss[i]=ss[i-7]+“8”。
代码:
#include
#include
#include
#include
using namespace std;
//找用 n 根火柴能拼出的最大的数
string largest(int n) {
string str;
int cnt = n/2, cmp = n%2;
//如果有偶数根火柴,就全部拼成数字 1
if(!cmp) {
for(int i = 0; i < cnt; i++) str += '1';
}
//如果有奇数根火柴,则最高位数字为 7,其余的均为 1
else {
str += '7';
for(int i = 1; i < cnt; i++) str += '1';
}
return str;
}
int main() {
int t;
cin >> t;
string ss[110]; //用于存能拼成的最小数字
//先将前面一些数据能拼成的最小数字例举出来
ss[0] = "1", ss[1] = "7", ss[2] = "4", ss[3] = "2";
ss[4] = "6", ss[5] = "8", ss[6] = "10", ss[7] = "18";
ss[8] = "22", ss[9] = "20", ss[10] = "28";
ss[11] = "68", ss[12] = "88";
for(int i = 13; i <= 98; i++) {
//容易错的地方:按规律 ss[15]="228",但可以拼成"200"
if(i == 15) ss[i] = "200";
else { //其余的就根据前面的数字来找,尽量拼成数字 8
ss[i] += ss[i-7];
ss[i] += '8';
}
}
while(t--) {
int n;
cin >> n;
cout << ss[n-2] << ' ' << largest(n) << endl;
}
return 0;
}
第三题:Sicily 1620 SCVs and minerals
题意:一开始你有N个农民和M单位的资源,每个农民一秒钟可以得到C单位的资源,每个农民可以用P单位的资源立即生产,而且农民采集资源的过程是离散的(例如不会在0.5秒的时候收入0.5C),问在S秒后所收集的最大资源是多少(不包括已经花出去的)。
解决思路:尽量多的产生农民,但前提是产生一个农民的代价小于他能生产的资源。
代码:
#include
using namespace std;
int main() {
int t;
cin >> t;
int N, M, C, P, S;
while(t--) {
cin >> N >> M >> C >> P >> S;
//可以产生农民的时间为 0~S-1 秒,而生产资源的时间为 1~S秒
for(int i = 0; i <= S; i++) {
if(i) M += N*C;
if((S-i)*C > P && i < S) { // 注意是 (S-i) 而不是 (S-i+1)
N += M/P;
M -= (M/P)*P;
}
}
cout << M << endl;
}
return 0;
}
第四题:Sicily 2503 最长字符串
#include
#include
#include
#include
#include
using namespace std;
//countA, countB, maxA, maxB都不为0时,以较小的数为分隔
//要根据两者之间数量的关系来确定第一小段的数量
int longest(int small, int large, int ms, int ml)
{
int cnt = large / small;
int res = 0;
//数量较多的每一小段的数量要与max比较
if(cnt > ml) cnt = ml;
if(large <= cnt*small + ml) res = small + large;
else res = (cnt + 1) * small + ml;
return res;
}
int main()
{
int countA, countB, maxA, maxB;
cin >> countA >> countB >> maxA >> maxB;
if(maxA && maxB) {
if(countA && countB) {//countA, countB, maxA, maxB都不为 0,以数量小的为分隔
if(countA <= countB) cout << longest(countA, countB, maxA, maxB) << endl;
else cout << longest(countB, countA, maxB, maxA) << endl;
}
else if(!countA && countB) {//有一个数量为 0时,比较另一个与最大连续数目的大小
if(countB <= maxB) cout << countB << endl;
else cout << maxB << endl;
}
else if(!countB && countA) {
if(countA <= maxA) cout << countA << endl;
else cout << maxA << endl;
}
else cout << 0 << endl; //两者数量都为 0时,直接输出 0
}
else if(!maxA && maxB) { //maxA为 0时,比较countB与maxB的大小
if(countB <= maxB) cout << countB << endl;
else cout << maxB << endl;
}
else if(!maxB && maxA) {
if(countA <= maxA) cout << countA << endl;
else cout << maxA << endl;
}
else cout << 0 << endl; //maxA,maxB都为 0,输出为 0
return 0;
}