贪心算法小结1

A-金银岛

  某天KID利用飞行器飞到了一个金银岛上,上面有许多珍贵的金属,KID虽然更喜欢各种宝石的艺术品,可是也不拒绝这样珍贵的金属。但是他只带着一个口袋,口袋至多只能装重量为w的物品。岛上金属有s个种类, 每种金属重量不同,分别为n 1, n 2, ... , n s,同时每个种类的金属总的价值也不同,分别为v 1,v 2, ..., v s。KID想一次带走价值尽可能多的金属,问他最多能带走价值多少的金属。注意到金属是可以被任意分割的,并且金属的价值和其重量成正比。

Input

   第1行是测试数据的组数k,后面跟着k组输入。 
  每组测试数据占3行,第1行是一个正整数w (1 <= w <= 10000),表示口袋承重上限。第2行是一个正整数s (1 <= s <=100),表示金属种类。第3行有2s个正整数,分别为n 1, v 1, n 2, v 2, ... , n s, v s分别为第一种,第二种,...,第s种金属的总重量和总价值(1 <= n i <= 10000, 1 <= v i <= 10000)。 

Output

  k行,每行输出对应一个输入。输出应精确到小数点后2位。

Sample Input

 

2
50
4
10 100 50 30 7 34 87 100
10000
5
1 43 43 323 35 45 43 54 87 43

Sample Output
171.93
508.00
  贪心问题,要注意题中说金属是可以任意分割的,且金属的价值和其质量成正比。所以要求出单位质量金属的价值,再排序。还有就是输出格式,要输出小数点后两位。代码如下:
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
int const max_n = 200;
struct node{
    int n,v;
    double p;
}s[max_n];
bool cmp(node a,node b)
{
    return a.p>b.p;
}
int main()
{
    int k;
    scanf("%d",&k);
    while(k--)
    {
        int w=0,S;
        double ans=0;
        scanf("%d",&w);
        scanf("%d",&S);
        for(int i=0;i<S;i++)
        {
            cin>>s[i].n>>s[i].v;
            if (s[i].n < 1 || s[i].n>10000 || s[i].v < 1 || s[i].v>10000)return 0;
            s[i].p=double(s[i].v)/s[i].n;
        }
        sort(s,s+S,cmp);
        for(int i=0;i<S;i++)
        {
            if(w>=s[i].n)
            {
                w-=s[i].n;
                ans+=s[i].v;
            }
            else {
                ans+=w*s[i].p;
                w=0;
            }
            if(w==0)break;
        }
        printf("%.2f\n",ans);
    }
    return 0;
}

 

B - Tian Ji -- The Horse Racing

Here is a famous story in Chinese history. 

That was about 2300 years ago. General Tian Ji was a high official in the country Qi. He likes to play horse racing with the king and others. 

Both of Tian and the king have three horses in different classes, namely, regular, plus, and super. The rule is to have three rounds in a match; each of the horses must be used in one round. The winner of a single round takes two hundred silver dollars from the loser. 

Being the most powerful man in the country, the king has so nice horses that in each class his horse is better than Tian's. As a result, each time the king takes six hundred silver dollars from Tian. 
Tian Ji was not happy about that, until he met Sun Bin, one of the most famous generals in Chinese history. Using a little trick due to Sun, Tian Ji brought home two hundred silver dollars and such a grace in the next match. 
It was a rather simple trick. Using his regular class horse race against the super class from the king, they will certainly lose that round. But then his plus beat the king's regular, and his super beat the king's plus. What a simple trick. And how do you think of Tian Ji, the high ranked official in China? 

 

Were Tian Ji lives in nowadays, he will certainly laugh at himself. Even more, were he sitting in the ACM contest right now, he may discover that the horse racing problem can be simply viewed as finding the maximum matching in a bipartite graph. Draw Tian's horses on one side, and the king's horses on the other. Whenever one of Tian's horses can beat one from the king, we draw an edge between them, meaning we wish to establish this pair. Then, the problem of winning as many rounds as possible is just to find the maximum matching in this graph. If there are ties, the problem becomes more complicated, he needs to assign weights 0, 1, or -1 to all the possible edges, and find a maximum weighted perfect matching... 
However, the horse racing problem is a very special case of bipartite matching. The graph is decided by the speed of the horses -- a vertex of higher speed always beat a vertex of lower speed. In this case, the weighted bipartite matching algorithm is a too advanced tool to deal with the problem. 
In this problem, you are asked to write a program to solve this special case of matching problem.

Input

The input consists of up to 50 test cases. Each case starts with a positive integer n ( n<=1000) on the first line, which is the number of horses on each side. The next n integers on the second line are the speeds of Tian's horses. Then the next n integers on the third line are the speeds of the king's horses. The input ends with a line that has a single `0' after the last test case.

Output

For each input case, output a line containing a single number, which is the maximum money Tian Ji will get, in silver dollars.

Sample Input

3
92 83 71
95 87 74
2
20 20
20 20
2
20 19
22 18
0
Sample Output
200
0
0
  题目大意:前面讲的是田忌赛马的故事,本题是故事套路的延伸,最多进行50个样例测试,第一行输入双方拥有的马匹数,第二行输入田忌的马匹的速度,第三行输入齐王的马匹速度,
马匹间两两进行赛跑,输的一方要给赢得一方200银元。输出田忌能赢或输多少银元。
  思路:依旧是使用贪心算法,首先对马匹速度进行排序,不断选取最优解,直至解决问题 。那么最优解是什么呢?两匹马进行赛跑,只有三种结果,田忌胜,齐王胜,平局;所以要尽
可能使用田忌的慢马消耗齐王的快马,从双方的慢马开始比较,若田忌的慢马比齐王的慢马快,双方用当前最慢的马匹进行赛跑,田忌胜场加一;反之使用田忌当前的慢马与齐王当前最快的
马进行比赛,田忌负场加一;如果双方此时最慢的马匹速度相等,可以分为两种情况,一:若此时田忌最快的马比齐王最快的马快,使用双方最快的马赛跑,田忌胜场加一,二:使用田忌此
时最慢的马与齐王最快的马赛跑,田忌负场加一。这样能最大程度削弱齐王的马匹优势。代码如下:
#include <iostream>
#include <algorithm>
#include <string.h>
#define LL long long
const int max_n=1002;
using namespace std;
int tian[max_n];
int king[max_n];
bool cmp(int a,int b)
{
    if(a==b)return a;
    return a>b;
}
int main()
{
    int k=50;
    while(k--)
    {
        int n;
        scanf("%d",&n);
        if(n==0)break;
        for(int i=0;i<n;i++)scanf("%d",&tian[i]);
        for(int i=0;i<n;i++)scanf("%d",&king[i]);
        sort(tian,tian+n);
        sort(king,king+n);
        int j=0,t=0;
        int tb=0,tend=n-1;
        int kb=0,kend=n-1;
        for(;tb<=tend;)
        {
            if(tian[tb]>king[kb])
            {
                tb++;
                kb++;
                j++;
            }
            else if(tian[tb]<king[kb])
            {
                tb++;
                kend--;
                t++;
            }
            else{
                if(tian[tend]>king[kend])
                {
                    tend--;
                    kend--;
                    j++;
                }
                else {
                    if(tian[tb]<king[kend])t++;
                    kend--;
                    tb++;
                }
            }
        }
        n=j-t;
        printf("%d\n",n*200);
        memset(tian,0,sizeof(tian));
        memset(king,0,sizeof(king));
    }
    return 0;
}

 

C - 最小新整数

 给定一个十进制正整数n(0 < n < 1000000000),每个数位上数字均不为0。n的位数为m。
现在从m位中删除k位(0<k < m),求生成的新整数最小为多少?
例如: n = 9128456, k = 2, 则生成的新整数最小为12456

Input

第一行t, 表示有t组数据; 
接下来t行,每一行表示一组测试数据,每组测试数据包含两个数字n, k。

Output

t行,每行一个数字,表示从n中删除k位后得到的最小整数。

Sample Input

2
9128456 2
1444 3
Sample Output
12456
1
  思路:数据可以用string来输入输出,这样可以避免来回转换类型出现的问题。依旧是贪心问题,最优解为:若a[i]>a[i+1](注意边界),则删除a[i];即从输入的数高位开始比较,因为输入
到了字符串中,所以自string a[0]开始遍历,没当其高位数比低位数大时,删除高位字符,删除使用a.erase(a.find(a[i]),1),删除一次计数变量+1,当计数变量与要删除的k值相等时或循环
结束后跳出循环;这时有两种情况,一:已经删够两位了,此时直接输出删后的string即可,二:还没有删除够k位(因为输入的数自高位到低位可能是递增的),此时找到剩余串中的最大值并删除,删除够
k位即可。代码如下:
 
#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
using namespace std;

string compare(string a, int k)
{

    int b = 0;
    for (int i = 0; i < a.length(); i++)
    {
        if (a[i] > a[i + 1]) {a.erase(a.find(a[i]), 1); b++;}
        if (b == k)break;
    }
    int t = k - b;
    if (t != 0)
    {
        while (t--)
        {
            char td='0';
            for (int i = 0; i < a.length(); i++)
            {
                td = max(td, a[i]);
            }
            a.erase(a.find(td), 1);
        }
    }
    return a;
}

int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        string n;
        int k;
        cin >> n;
        scanf("%d", &k);
        string s = compare(n, k);
        cout << s << endl;
    }
    return 0;
}
 
 

 D-Allowance

作为创纪录的牛奶生产的奖励,农场主约翰决定开始给Bessie奶牛一个小的每周津贴。FJ有一套硬币N种(1≤N≤20)不同的面额,每枚硬币是所有比他小的硬币面值的倍数,例如1美分硬币、5美分硬币、10美分硬币和50美分硬币。使用这些硬币,FJ每周至少给Bessie C(1 <= C <=100000000)美分。请你计算他最多能给Bessie几周

Input

* 第一行N 、 C 

* 第 2..N+1行: 硬币价值 V (1 <= V <= 100,000,000) 和数量 B (1 <= B <= 1,000,000)

Output

* 使用这些硬币,每周至少给C美分的前提下的最多周数

Sample Input

3 6
10 1
1 100
5 120

Sample Output

111

Hint


输出提示 
FJ给Bessie 10美分一周;给Bessie 5美分和1美分一百周;给Bessie 两枚5美分十周
  思路:题中问最多能给多少个星期,也就是用有限的钱,让Bessie干最长时间的工(可以多给钱,但一定不能少给,不然Bessie就罢工了)。首先排序,这题的处理分为两个阶段,一:当该货币的价值大于最低津贴时,每次付一张该货币即可,计数器加上该价值货币的数量。直到货币价值低于最低津贴时,第二阶段开始了。二:此时,应该想办法使用最少的货币数量去支付津贴,那就优先使用最大的货币去多次支付,直到与剩余需支付津贴低于该货币价值,而不足部分,由低价值货币补充,并且应该从价值最低的货币开始补充(这样最可能使支付的钱刚好与津贴相等),直到已经足够支付津贴,计数器加一(要注意每使用一次某种货币,其数量要减一);开始下一轮。当FJ剩余的钱不足以支付一周的最低津贴时,此时计数器大小为最多周数。代码如下:
  
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
struct node {
    int v, b;//货币价值,数量
}num[22];//货币种类最多20种
bool cmp(node a, node b)//定义结构体排序方法
{
    return a.v > b.v;
}
int main()
{
    int n, c, ans = 0;
    scanf("%d %d", &n, &c);
    for (int i = 0; i < n; i++)
    {
        scanf("%d %d", &num[i].v, &num[i].b);
    }
    sort(num, num + n, cmp);//降序排列
    int k = 0;
    for (k=0; k < n; k++)
    {
        if (num[k].v >= c)//当货币价值大于津贴时
        {
            ans += num[k].b;
        }
        else break;
    }
    bool judge = true;
    while(judge){
        judge = false;
        int s = c;
        for (int i = k; i < n; i++)//自货币价值小于津贴的位置开始,向更小的遍历
        {
            while (num[i].b > 0 && num[i].v < s)
            {//使用尚未支付完的货币中的价值最大的支付,且尽量不支付超过津贴的钱
                s -= num[i].v;
                num[i].b--;
            }
        }
        for (int j = n - 1; j >= k; j--)
        {//优先使用价值小货币,补齐尾款,尽可能不多付钱。
            while (s > 0 && num[j].b > 0)
            {
                s -= num[j].v;
                num[j].b--;
            }
        }
        if (s<=0)//通过两边交叉付款后,若已经付清津贴,则计数器加1
        {
            judge = true;
            ans++;
        }
    }
    printf("%d", ans);
    return 0;
}

 E-Best Cow Line

 

给定长度为N(1≤N≤2000)的字符串S,要构造一个长度为N的字符串T。期初,T是一个空串,随后反复进行下列任意操作。

·从S的头部删除一个字符,加到T的尾部

·从S的尾部删除一个字符,加到T的尾部

目标是要构造字典序尽可能小的字符串

Input

· Line 1: 一个整数(integer): N
· Lines 2~+1: Line i+1 contains a single initial ('A'..'Z') of the string in the ith position in the original line

Output

输出时每行最多80个字符

Sample Input
6
A
C
D
B
C
B
Sample Output
ABCBCD
  解题思路:构建最小字典序字符串,每次取最优解即可,即比较输入的串的两端,取字典序小的那一个字符;而当两个字符相等时,开始比较下一位,返回较小的那一段。具体实现是使用一个bool型变量
确定到底哪一端的字符较小。(注意一下输出,每行最多输出80个字符,输够80个字符后记得换行)代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <stack>
#include <utility>
using namespace std;
int const max_n=2005;
int main()
{
    int n;
    scanf("%d",&n);
    char s[max_n],T[max_n];
    for(int i=0;i<n;i++)cin>>s[i];
    int a=0,b=n-1,c=0;
    while(a<=b)
    {
        bool left=false;//设置left,当left为真时,左端字典序小于右端
        for(int i=0;a+i<=b;i++)//这样设置的好处是,可以进行左右两端的比较,且下标都会改变
        {
            if(s[a+i]<s[b-i]){//a+i,b-i;两端的下标
                left=true;
                break;
            }
            else if(s[a+i]>s[b-i]){
                left=false;
                break;
            }
        }
        if(left){
            T[c++]=s[a++];
            cout<<T[c-1];
        }
        else {
            T[c++]=s[b--];
            cout<<T[c-1];
        }
        if(c%80==0)cout<<endl;//注意换行
    }
    cout<<endl;
    return 0;
}

 

 
  
 
 
 
 

 

转载于:https://www.cnblogs.com/whocarethat/p/11004586.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值