JZOJ 1217 软件开发 二分+DP
2016-08-08 21:00
211 查看
Problem
Description
一个软件开发公司同时要开发两个软件,并且要同时交付给用户,现在公司为了尽快完成这一任务,将每个软件划分成m个模块,由公司里的技术人员分工完成,每个技术人员完成同一软件的不同模块的所用的天数是相同的,并且是已知的,但完成不同软件的一个模块的时间是不同的,每个技术人员在同一时刻只能做一个模块,一个模块只能由一个人独立完成而不能由多人协同完成。一个技术人员在整个开发期内完成一个模块以后可以接着做任一软件的任一模块。写一个程序,求出公司最早能在什么时候交付软件。
Input
输入文件第一行包含两个由空格隔开的整数n和m,其中1<=n<=100,1<=m<=100,接下来的n行每行包含两个用空格隔开的整数d1和d2,d1表示该技术人员完成第一个软件中的一个模块所需的天数,d2表示该技术人员完成第二个软件中的一个模块所需的天数,其中1<= d1,d2<=100。
Output
输出文件仅有一行包含一个整数d,表示公司最早能于d天后交付软件。
Sample Input
3 20
1 1
2 4
1 6
Sample Output
18
Hint
【样例解释】
最快的方案是第一个技术人员完成第二个软件的18个模块,用时18天,第三个技术人员完成第一个软件的18个模块,用时18天,其余的模块由第二个技术人员完成,用时12天,做完所有模块需要18天。如果第一个技术人员完成第二个软件的17个模块,第三个技术人员完成第一个软件的17个模块,其余的模块由第二个技术人员完成,需要用时18天,做完所有模块仍然需要18天,所以少于18天不可能做完所有模块。
直接DP需要五层循环,f[j,k]代表做第一个软件j个模块、第二个软件k个模块,最少花费的时间。第一层循环枚举每一个人,二、三层循环枚举第一、二个软件已经做了多少模块,第四、五层循环枚举第i个人第一、二个软件做了多少模块。
状态转移方程为:f[j,k]=max(f[j,k],f[j-t,k-u]+a[i]*t+b[i]*u)
这样做可以得到60分。来想一想,如何才能减少循环呢?如果要是知道时间就好了,这样只要枚举第一个软件完成的模块就可以计算出第二个模块完成了多少。这道题需要求最短时间,答案具有单调性,可以先二分答案,再通过DP来验证答案的正确性。状态变为:f[i,j]表示前i个人做了第一个软件j个模块最多做第二个软件多少模块。
具体的状态转移方程为:f[i,j]=max(f[i,j],f[i-1,j-k]+(x-a[i]*k)/b[i])
最后看f[n,m]如果大于m即合法,循环层数降为三层。需要注意的是状态的处理,初值要赋成-1,表示这个状态没有产生。再把f[0,0]赋成0,表示这一状态合法。如果用来更新的状态没有产生就不能用这个状态来更新当前状态。
Description
一个软件开发公司同时要开发两个软件,并且要同时交付给用户,现在公司为了尽快完成这一任务,将每个软件划分成m个模块,由公司里的技术人员分工完成,每个技术人员完成同一软件的不同模块的所用的天数是相同的,并且是已知的,但完成不同软件的一个模块的时间是不同的,每个技术人员在同一时刻只能做一个模块,一个模块只能由一个人独立完成而不能由多人协同完成。一个技术人员在整个开发期内完成一个模块以后可以接着做任一软件的任一模块。写一个程序,求出公司最早能在什么时候交付软件。
Input
输入文件第一行包含两个由空格隔开的整数n和m,其中1<=n<=100,1<=m<=100,接下来的n行每行包含两个用空格隔开的整数d1和d2,d1表示该技术人员完成第一个软件中的一个模块所需的天数,d2表示该技术人员完成第二个软件中的一个模块所需的天数,其中1<= d1,d2<=100。
Output
输出文件仅有一行包含一个整数d,表示公司最早能于d天后交付软件。
Sample Input
3 20
1 1
2 4
1 6
Sample Output
18
Hint
【样例解释】
最快的方案是第一个技术人员完成第二个软件的18个模块,用时18天,第三个技术人员完成第一个软件的18个模块,用时18天,其余的模块由第二个技术人员完成,用时12天,做完所有模块需要18天。如果第一个技术人员完成第二个软件的17个模块,第三个技术人员完成第一个软件的17个模块,其余的模块由第二个技术人员完成,需要用时18天,做完所有模块仍然需要18天,所以少于18天不可能做完所有模块。
直接DP需要五层循环,f[j,k]代表做第一个软件j个模块、第二个软件k个模块,最少花费的时间。第一层循环枚举每一个人,二、三层循环枚举第一、二个软件已经做了多少模块,第四、五层循环枚举第i个人第一、二个软件做了多少模块。
状态转移方程为:f[j,k]=max(f[j,k],f[j-t,k-u]+a[i]*t+b[i]*u)
这样做可以得到60分。来想一想,如何才能减少循环呢?如果要是知道时间就好了,这样只要枚举第一个软件完成的模块就可以计算出第二个模块完成了多少。这道题需要求最短时间,答案具有单调性,可以先二分答案,再通过DP来验证答案的正确性。状态变为:f[i,j]表示前i个人做了第一个软件j个模块最多做第二个软件多少模块。
具体的状态转移方程为:f[i,j]=max(f[i,j],f[i-1,j-k]+(x-a[i]*k)/b[i])
最后看f[n,m]如果大于m即合法,循环层数降为三层。需要注意的是状态的处理,初值要赋成-1,表示这个状态没有产生。再把f[0,0]赋成0,表示这一状态合法。如果用来更新的状态没有产生就不能用这个状态来更新当前状态。
#include <cstdio> #include <cstring> #define max(a,b) (a>b?a:b) using namespace std; int n,m,a[101],b[101],f[101][101]; bool check(int x){ memset(f,-1,sizeof f); f[0][0]=0; for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) for(int k=0;k<=j;k++) if(x-a[i]*k>=0 && f[i-1][j-k]!=-1) f[i][j]=max(f[i][j],f[i-1][j-k]+(x-a[i]*k)/b[i]); if(f [m]>=m) return true; return false; } int main(){ freopen("software.in","r",stdin); freopen("software.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]); int l=1,r=10000; while(l+1<r){ int mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid; } int ans; if(check(l)) ans=l; else ans=r; printf("%d\n",ans); fclose(stdin); fclose(stdout); return 0; }
相关文章推荐
- 【三校联考试题】软件开发 二分+dp
- JZOJ1217. 软件开发(2017年8月B组)
- JZOJ. 1217. 软件开发
- NOIP模拟试题 软件开发(二分DP)
- 20160807 软件开发 二分暴力
- [排序][dp][二分]JZOJ 2746 选数排列
- jzoj 2746.【2012中山市选】选数排列(pick) 二分答案+dp
- [排序][二分][dp]JZOJ 2747 捡金子
- 微软公司软件开发模式简介
- 恶魔和梦魇的私语------- 关于软件开发的务虚主义对话(4)
- 恶魔和梦魇的私语------- 关于软件开发的务虚主义对话(1)
- COM应用软件开发技术 (bcd的文章)
- 三维图形及动画软件开发流程
- 开发通信软件的技术与技巧
- 变革与突破—如何认识基于构件的软件开发方法
- 谈谈软件开发中的调研对象与被调研对象
- 国内应用软件开发管理的探讨
- 恶魔和梦魇的私语------- 关于软件开发的务虚主义对话(2)
- 美国某大型软件的开发计划日程安排
- USB开发步骤之软件篇