poj 1973 二分时间+DP

一开始自己写了一个状态转移方程

用res[i][j][k]表示前i个人已经完成A的j个部分B的k个部分所有过的最少时间。

res[i][j][k]=min(res[i][j][k],max(res[i-1][j-s][k-t],s*A[i]+t*B[i]));

很显然时间复杂度为O(n^5),显然是不可取的。

看了大牛的思想后才知道可以用二分时间+DP来优化:

令res[i][j]表示前i个人完成A的j个部分最多能完成B多少。

res[i][j]=max(res[i][j],res[i-1][j-k]+(mid-k*A[i])/B[i]);当res[i][j]>=m 说明前i个人可以将任务完成,因此二分时间不断试探可以完成任务的时间,最终确定最小完成时间。时间复杂度就变为O(n^3)*log(r)(r表示为最大完成时间);值得注意的是:当前i个人已经能够完成任务,就直接可以返回true了,这时一个很好的优化条件。

因为res[i][j]只与res[i-1][k]有关,所以可将空间复杂度将为一维。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int res[110],A[110],B[110],l[110];
inline int max(int a,int b)
{
return a > b ? a : b;
}
int dp(int n,int m,int mid)
{
int i,j,k;
memset(res,-1,sizeof(res));
for(j=0;j<=m;j++)
{
if(mid < j*A[1]) break;
res[j]=(mid-j*A[1])/B[1];
}
if(res[m]>=m) return 1;
for(i=2;i<=n;i++)
{
for(j=m;j>=0;j--)
for(k=0;k<=j;k++)
{
if(mid < k*A[i]) break;
if(res[j-k]!=-1) res[j]=max(res[j],res[j-k]+(mid-k*A[i])/B[i]);
}
if(res[m]>=m) return 1;
}
return 0;
}
int work(int n,int m,int Max)
{
int head,tail,mid;
head=0; tail=Max;
while(head < tail)
{
mid=(head+tail)>>1;
if(dp(n,m,mid))
tail=mid;
else
head=mid+1;
}
return head;
}
int main()
{
int i,m,n,Case,maxn;
scanf("%d",&Case);
while(Case--)
{
scanf("%d %d",&n,&m);
maxn=0;
for(i=1;i<=n;i++)
{
scanf("%d %d",&A[i],&B[i]);
maxn=max(maxn,max(A[i],B[i]));
}
printf("%d\n",work(n,m,maxn*2*m));
}
return 0;

}



你可能感兴趣的:(poj)