贪心算法及相关题目(一)

一、贪心算法概念

贪心算法通过做出一系列选择来求出问题的最优解,在每个决策点,它做出在当时看来最佳的选择。由此可见,贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择,但是对许多问题它都所得到的解都是全局最优解。

二、相关题目

第一题:Sicily 1198. Substring

  • 题目及输入输出:
    贪心算法及相关题目(一)_第1张图片

  • 样例:
    贪心算法及相关题目(一)_第2张图片

  • 题意:给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

  • 题目:
    贪心算法及相关题目(一)_第3张图片

  • 输入输出及样例:
    贪心算法及相关题目(一)_第4张图片

  • 解决思路:先从数字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

  • 题目:
    贪心算法及相关题目(一)_第5张图片

  • 样例:
    贪心算法及相关题目(一)_第6张图片

  • 题意:一开始你有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 最长字符串

  • 题目及输入输出:
    贪心算法及相关题目(一)_第7张图片

  • 样例:
    贪心算法及相关题目(一)_第8张图片

  • 解决思路:以总数量较小的字符为分隔,根据二者数量的关系来确定数量较大的字符在每小段的数量,注意要与最大连续字符数量比较,还要注意各个数为0的情况。

  • 代码:

#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;
}

你可能感兴趣的:(贪心算法及相关题目(一))