[luogu]P1800 software_NOI导刊2010提高(06)[DP][二分答案]

[luogu]P1800

software_NOI导刊2010提高(06)

题目描述

一个软件开发公司同时要开发两个软件,并且要同时交付给用户,现在公司为了尽快完成这一任务,将每个软件划分成m个模块,由公司里的技术人员分工完成,每个技术人员完成同一软件的不同模块的所用的天数是相同的,并且是已知的,但完成不同软件的一个模块的时间是不同的,每个技术人员在同一时刻只能做一个模块,一个模块只能由一个人独立完成而不能由多人协同完成。一个技术人员在整个开发期内完成一个模块以后可以接着做任一软件的任一模块。写一个程序,求出公司最早能在什么时候交付软件。

输入输出格式

输入格式:

输入文件第一行包含两个由空格隔开的整数n和m,其中1<=n<=100,1<=m<=100,接下来的n行每行包含两个用空格隔开的整数d1和d2,d1表示该技术人员完成第一个软件中的一个模块所需的天数,d2表示该技术人员完成第二个软件中的一个模块所需的天数,其中1<= d1,d2<=100。

输出格式:

输出文件仅有一行包含一个整数d,表示公司最早能于d天后交付软件。

输入输出样例

输入样例1#:

3 20
1 1
2 4
1 6

输出样例1#:

18

说明

【样例】

最快的方案是第一个技术人员完成第二个软件的18个模块,用时18天,第三个技术人员完成第一个软件的18个模块,用时18天,其余的模块由第二个技术人员完成,用时12天,做完所有模块需要18天。如果第一个技术人员完成第二个软件的17个模块,第三个技术人员完成第一个软件的17个模块,其余的模块由第二个技术人员完成,需要用时18天,做完所有模块仍然需要18天,所以少于18天不可能做完所有模块。


一道神奇的题目,容易想到答案是单调的,考虑二分答案。

但是检验...一开始要贪心的,想想完全不行啊啊啊!!!

可恶,偷看一下神犇的题解,真的牛!

每个人其实相互独立,各干各的,最后求他们的最大值,最小化最大值。

所以我们只需要让n个人第一个做满,最大化第二个,如果>=m,则可以。

所以我们用f[i][j]表示前i个人第一个软件做了j个,可以最多做的第二个软件。

转移:
f[i][j]=Max{f[i-1][j-k]+(mid-d1[i]*k)/d2[i]} 【如果第i个选择了k个,只剩下(mid-d1[i])/d2[i]个可以做(将每个决策独立出来,好方法,NB。)】

还有就是可恶的边界,不存在的状态要初始化一个很小的数。f[0][0]=0;

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 inline int read();
 6 int Max(int x,int y){return x>y?x:y;}
 7 int Min(int x,int y){return x<y?x:y;}
 8 namespace lys{
 9     const int N = 1e2 + 7 ;
10     int dp[N][N],d1[N],d2[N];
11     int n,m;
12     bool chk(int mid){
13         int i,j,k;
14         memset(dp,-127,sizeof dp);
15         dp[0][0]=0;
16         for(i=1;i<=n;i++)
17             for(j=0;j<=m;j++)
18                 for(k=0;k<=Min(j,mid/d1[i]);k++) dp[i][j]=Max(dp[i][j],dp[i-1][j-k]+(mid-d1[i]*k)/d2[i]);
19         return (dp[n][m]>=m);
20     }
21     int main(){
22         int i;
23         n=read(); m=read();
24         for(i=1;i<=n;i++) d1[i]=read(),d2[i]=read();
25         int l=1,r=20000,mid;
26         while(l<r){
27             mid=(l+r)>>1;
28             if(chk(mid)) r=mid;
29             else l=mid+1;
30         }
31         printf("%d\n",l);
32         return 0;
33     }
34 }
35 int main(){
36     lys::main();
37     return 0;
38 }
39 inline int read(){
40     int kk=0,ff=1;
41     char c=getchar();
42     while(c<'0'||c>'9'){
43         if(c=='-') ff=-1;
44         c=getchar();
45     }
46     while(c>='0'&&c<='9') kk=kk*10+c-'0',c=getchar();
47     return kk*ff;
48 }

 

转载于:https://www.cnblogs.com/Liisa/p/7805991.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值