您的位置:首页 > 其它

【三校联考试题】软件开发 二分+dp

2017-09-14 19:59 225 查看
【问题描述】

  小沐的软件开发公司同时要开发两个软件,并且要同时交付给用户。为了尽快完成这一任务,他将每个软件划分成m个模块,由公司里的技术人员分工完成。

  每个技术人员完成同一软件的不同模块的所用的天数是相同的,并且是已知的,但完成不同软件的一个模块的时间是不同的,每个技术人员在同一时刻只能做一个模块,一个模块只能由一个人独立完成而不能由多人协同完成。一个技术人员在整个开发期内完成一个模块以后可以接着做任一软件的任一模块。

  小沐请你帮忙写一个程序计算应怎样分配每个技术人员的任务,才能让这两个软件的交付时间尽量早。

【输入格式】

  第一行包含两个由空格隔开的整数n和m,其中1≤n≤100,1≤m≤100。

  接下来的n行每行包含两个用空格隔开的正整数a和b,其中第i+1行的a表示技术人员i完成第一个软件中的一个模块所需的天数,b表示该技术人员完成第二个软件中的一个模块所需的天数,其中l≤a,b≤100。

【输出格式】

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

【输入样例】

3 20

1 1

2 4

1 6

【输出样例】

18

【数据范围】

对于30%的数据:1≤n≤10,1≤m≤10  

对于50%的数据:1≤n≤50,1≤m≤50  

对于100%的数据,1≤n≤100,1≤m≤100,l≤a,b≤100

————————————————————————————————————————————————————————

看见这么赤裸裸的直接问天数贪心dp二分跑不了了。

贪心我发现是错的。。。(如果使用类似于“假如说先让第一个软件完成,那么这个时候先把员工按照a排序,然后每个员工尽量多地去完成第一个软件的模块。因为都是完成一个模块,所以说肯定是用最少的时间去完成一个模块来的划算。”这样的贪心来验证的话会有反例的情况:当前这个人完成a的时候突然不做了(这个时候a剩余的时间足够多),然后去做b(这种情况下这个人完成b的效率也很高),最后就可以完成软件开发。但是按照上面的策略有可能是不可以完成开发的。),a,b分别指代第一个软件和第二个软件。

那么来一发dp吧!

可以设出一个f(n,i,j)的式子表示前n个人完成前i个第一种软件和前j个第二种软件需要的最少时间,写出一个O(N*M^4)的算法手动再见。

那么需要简化决策。可以发现第一个贪心的方法错误的原因实际上是因为每个人可以在两个软件中分别选择一些模块出来。那么只需要知道这个人工作了多久时间,就可以在知道他在第一个软件上完成了几个模块的情况下计算出他在第二个软件商完成的模块数目。

于是二分最终的时间mid,设:f(i,j)表示在已知时间为mid的情况下前i个人完成j个第一个软件的模板时完成最多第二个软件模板的数量f(i,j)=max{ f(i-1,jj) + (mid-(j-jj)* c[i].a)/c[i].b | (j-jj)*c[i].a<=mid,0<=jj<=j }

二分的范围显然是[1,20000],dp的时候注意状态函数的意义。先把整张dp表赋成-inf,f[0][0]=0,i==1的时候需要特殊处理,其他的直接dp就可以了。

如果f(N,M)>=M返回true,否则返回false。

时间复杂度为O(log2(20000) * N * M^2);

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
const int maxn=105;

int N,M;
struct data{ int a,b; }c[maxn];
int f[maxn][maxn],ans;

void data_in()
{
scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++)
scanf("%d%d",&c[i].a,&c[i].b);
}
bool check(int mid)
{
memset(f,-20,sizeof(f));
f[0][0]=0;
for(int j=0;j<=M;j++)
if((mid-j*c[1].a)/c[1].b>=0) f[1][j]=(mid-j*c[1].a)/c[1].b;
for(int i=2;i<=N;i++)
{
for(int j=1;j<=M;j++)
for(int jj=j;jj>=0&&(j-jj)*c[i].a<=mid;jj--)
f[i][j]=max(f[i][j],f[i-1][jj]+(mid-(j-jj)*c[i].a)/c[i].b);
}
return f
[M]>=M;
}
void work()
{
int L=1,R=20001,mid;
while(L<R)
{
mid=L+R>>1;
if(check(mid)) R=mid,ans=mid;
else L=mid+1;
}
printf("%d\n",ans);
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
data_in();
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: