第四周总结——贪心算法2

一:

贪心的知识点上次大多写过,这里不重复。这周做题和看一些网站上题的时候会发现其实贪心题目大多要用到排序和优先队列。贪心和动态规划碰到题目会傻傻分不清,最近我还要多多学习这部分内容。

不多说,直接上题目吧

二:

例题一:
描述
We have received an order from Pizoor Communications Inc. for a special communication system. The system consists of several devices. For each device, we are free to choose from several manufacturers. Same devices from two manufacturers differ in their maximum bandwidths and prices. 
By overall bandwidth (B) we mean the minimum of the bandwidths of the chosen devices in the communication system and the total price (P) is the sum of the prices of all chosen devices. Our goal is to choose a manufacturer for each device to maximize B/P.
输入
The first line of the input file contains a single integer t (1 ≤ t ≤ 10), the number of test cases, followed by the input data for each test case. Each test case starts with a line containing a single integer n (1 ≤ n ≤ 100), the number of devices in the communication system, followed by n lines in the following format: the i-th line (1 ≤ i ≤ n) starts with mi (1 ≤ mi ≤ 100), the number of manufacturers for the i-th device, followed by mi pairs of positive integers in the same line, each indicating the bandwidth and the price of the device respectively, corresponding to a manufacturer.
输出
Your program should produce a single line for each test case containing a single number which is the maximum possible B/P for the test case. Round the numbers in the output to 3 digits after decimal point.
刚看到这题,凭借我鄙陋的英文水平翻译了一下,妈呀,这题不就是性价比问题吗,每一行一个厂家,找出最大的,然后比较所有厂家中最大的不就OK了?代码敲出来发现没有这个答案,郁闷喽,不得不借助翻译工具了。
一,题目大意:
一个通信系统有n种设备组成,现有这n种设备,每种设备有不同价格以及性能的不同(量化为b),要求是每种设备中选择一个,记取出的n个设备中的最小性能值为B,它们的价格总和为P,要计算可能的最大性价比B/P。

理解大意后,又是最大又是最小,到底是最大还是最小啊,我的天哪,这代码来回删改,最后自己也蒙了,算了,全删了重敲吧。
记住了:1.这个网络系统的最大带宽取决于每个厂家的路由中的最小带宽。2.不是每一行代表一个厂家,每一列才代表一个厂家呢。大约如下表格这种理解吧

厂家厂家1厂家2厂家3
机器数b1   p1..........
3   
2   
2   


话不多说,上代码

第一次提交:wrong answer(崩溃了)

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>
using namespace std;
int band[105][105];
int price[105][105];
int x[105];
int main()
{
    int t;
    double sump;
    double love=0;//比列,开始是定义在后面,不知道为什么老是错误,就放到最前面吧。
    cin>>t;
    while(t--)
   {
       int n;
       cin>>n;
       int minband=999999;
       int maxband=0;
       for(int i=1;i<=n;i++)
       {
           cin>>x[i];
           for(int j=1;j<=x[i];j++)
           {
               cin>>band[i][j]>>price[i][j];
               minband=min(minband,band[i][j]);
               maxband=max(maxband,band[i][j]);
           }
       }
       //double sump;
       //double love=0;//比列
       for(int k=minband;k<=maxband;k++)
       {
          sump=0;
          for(int i=1;i<=n;i++)
          {
              int minp=999999;
              for(int j=1;j<=x[i];j++)
              {
                  if(band[i][j]>=k&&price[i][j]<minp)
                    minp=price[i][j];
              }
              sump+=minp;
          }
          if(k*1.0/sump>love)
            love=k*1.0/sump;
       }
   }
   cout<<fixed<<setprecision(3)<<love<<endl;
    return 0;
}

通过代码:(看了一些题解,用常变量定义maxn,double换成了float,最后保留三位小数的输出格式也换了,至于到底因为哪一点导致之前的出错,我也不知道)

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=100+8;
int band[maxn][maxn];
int price[maxn][maxn];
int x[maxn];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        int minband=999999;
        int maxband=0;
        for(int i=1;i<=n;i++)
        {
            cin>>x[i];
            for(int j=1;j<=x[i];j++)
            {
                cin>>band[i][j]>>price[i][j];
                minband=min(minband,band[i][j]);
                 maxband=max( maxband,band[i][j]);
            }
        }
        float love=0;//比列
        float sump=0;
        for(int k=minband;k<= maxband;k++)
        {
            sump=0;
            for(int i=1;i<=n;i++)
            {
                int minp=999999;
                for(int j=1;j<=x[i];j++)
                {
                    if(band[i][j]>=k&&price[i][j]<minp)
                    {
                        minp=price[i][j];
                    }
                }
                sump+=minp;
            }
            if(k*1.0/sump>love)
            {
                love=k*1.0/sump;
            }
        }
        printf("%.3f\n",love);
    }
    return 0;
}



例题2 .选课
题目大意:一共有N门课可以选,但选课时每次必须间隔5分钟。例如你在5分07秒选了一门课,那必须在10分07秒选下一门课.。每一门课的的选课是有时间限制的,你只能在Ai到Bi之间选中第i门课。求你能选择的课最多多少门
 
题目输入
先输入N,表示一共有N门课。
接下来N行,每行两个整数,为课程可以被选择中的时间段。开始时间和结束时间。输入0门课表示输入结束。
2
1 10
4 5
0
 
样例输出 
2
本题相当于区间问题,但关键难点在于隔5分钟才能选一次。
解题思路:可以把5分钟分成5个时间段,那么只需分别找到开始时间为0、1、2、3、4的选课方式中选课最多的即可。
如果某一时刻有多门课可以选择,则选择最早结束的课程。使用了vis数组来记录已经选过的课。确保不会重复选。
 

#include<iostream>
#include<algorithm>
using namespace std;
struct time
{
    int a;
    int b;
}N[1001];
bool cmp(time x,time y)
{
    if(x.b!=y.b) return x.b<y.b;
    else return x.a<y.a;
}
int main()
{
    int n,i;
    while(cin>>n)
    {
        if(n==0) break;
    for(i=0;i<n;i++)
        cin>>N[i].a>>N[i].b;
    sort(N,N+n,cmp);
    bool vis[400];
    int true_ans=0;
    
    //关键到了
    for(int start=0;start<5;start++)//5种情况,每时刻一个开始的情况
    {
        for(i=0;i<n;i++) vis[i]=false;
        int ans=0;
        for(int k=start;k<1001;k+=5)
          {
            for(int j=0;j<n;j++)
        {
            if(vis[j]) continue;
            if(k>=N[j].a&&k<N[j].b)
            {
                vis[j]=true;
                ans++;
                break;//每一时刻只能选一门课
            }
        }
         }
    true_ans=max(true_ans,ans);//5种情况下的一个最大值
    }
    cout<<true_ans<<endl;
}
    return 0;
}

例题3
题意:
有n头奶牛跑到FJ的花园里去吃花了,它们分别在距离牛圈T分钟处吃花,每分钟会吃掉D朵卡哇伊的花儿。
FJ现在要将它们给弄回牛圈,但是他每次只能弄一头回去,来回用时总共为2*T分钟,在这段时间内,其它的奶牛会继续吃FJ卡哇伊的花儿,速度保持不变,当然正在被赶回牛圈的奶牛就没口福了!
现在要求以一种最棒的方法来尽可能的减少花的损失数量,求奶牛吃掉花的最少朵数!
(这题预习的时候怎么也想不明白)
贪心思路:假设序列都由二元组组成,二元组是由D和T组成,那么对一个序列有相邻的两头牛是这样的:
..........(a, b) ,(c, d)....................
如果(a,b)和(c,d)交换位置了,变成新序列
..........(c,d),(a,b).....................
假设在这之前FJ已经花了x时间了。
那么赶完这两头牛的损失的量就分别为
先带走A x*b + (x + a ) * d
先带走B x*d +(x + c) * b
二者做差
得到ad - bc
若ad < bc 则有第一个序列优于第二个

理解:这题其实理解之后不难,关键排序是对乘积进行排序,时间要算来回的时间,(开始我就算一次)
 

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
    int x, y;//距离的时间与吃掉的花个数
}a[10001];
int n;
bool cmp(node x, node y)
{
    //升序排列
    return x.x * y.y < x.y * y.x;
}

int main()
{
    cin>>n;//n头奶牛
    for(int i = 0; i < n; i++)
    {
        cin>>a[i].x>>a[i].y;
        a[i].x *= 2;//来回用时
    }
    sort(a, a + n, cmp);
    long long ans = 0;//损失的总花数
    long long sum = 0;
    for(int i = 0; i < n; i++)
    {
        ans += sum * (long long)a[i].y;
        sum += a[i].x;
    }
   cout<< ans<<endl;
    return 0;
}

三。

也许天才不是每天都是天才,每个人也许都是天才,只是天才大脑灵敏清醒的时间比一般人长很多吧。有时候感觉自己做题很有状态,思路也很里清晰,无论上什么课都很轻松的跟上老师,但也有时候,读一个题好多遍也理解不了,上课走神不想听讲。今天是一个相对清醒的一天,所以就不说疯话了,做题去了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

季沐晴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值