动态规划练习1
题目描述:
在ls森林里有一只很能砍树的樵夫lw,他左手扛狂暴药水,右手握着雷神之斧,白天砍树,晚上喝狂暴药水。这天他接到了国王给他的一个任务:砍掉两颗树。设两颗树的耐久值都为x(各自独立)。而lw有n(n<=100)把不同的斧头,设第i把斧头每a[i]秒种能消耗第一棵树1点耐久值,或者每b[i]秒消耗第二棵树1点耐久值。对于一个斧头来说,砍树对象可以变换,即可以砍a[i]秒a,再砍b[i]秒b。对于一棵树来说,可以同时被多个斧头砍。当两颗树的耐久值都小于等于0时任务完成。lw想问你完成的最短时间是多少。
输入格式:
第一行两个数,分别为n和x,即斧头数和树的耐久值,接下来n行每行两个数,第i+1行的两个数为a[i]和b[i]。
输出格式:
第一行一个数t,即完成任务的最少时间。
样例输入:tree.in
2 20
5 1
1 5
样例输出:tree.out
20
数据范围:
对于30%的数据,n<=10,x<=30,a[i],b[i]<=20。
对于100%的数据,n<=100,x<=100,a[i],b[i]<=100。
题解:
考虑f[i][j]表示前i个斧头砍1的j块,还可以砍2的最多块数。
d[i][0]存砍1的效率,d[i][1]存砍2的效率
f[i][j]=max(f[i][j],f[i-1][j-k]+(day-d[i][0]*k)/d[i][1]);
f[i-1,j-k]是前i-1个斧头砍j-k个1还能砍几个2。day-d[i,0]*k是第i个斧头砍k个1还剩的时间
(day-d[i][0]*k)/d[i][1]第i个斧头还能砍几个2
所以f[i-1][j-k]+(mid-d[i][0]*k)/d[i][1]是前i个斧头一共可以砍几个2
显然是成立的。
而对于day的值,可以证明,f是随day单调递增的。我们可以二分这个day值,每次用DP检验。若f[n][x]>=x,r=mid-1;f[n][x]<x,l=mid+1。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int f[101][101],n,x,a[101],b[101]; int gi() { int ans=0,f=1; char i=getchar(); while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();} while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();} return ans*f; } bool judge(int mid) { int i,j,k; memset(f,200,sizeof(f)); f[0][0]=0; for(i=1;i<=n;i++) for(j=0;j<=x;j++) for(k=0;k<=min(j,mid/a[i]);k++) f[i][j]=max(f[i][j],f[i-1][j-k]+(mid-k*a[i])/b[i]); return f[n][x]>=x; } int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); int i,j; n=gi();x=gi(); for(i=1;i<=n;i++){a[i]=gi();b[i]=gi();} int l=0,r=20000,ans=0; while(l<=r) { int m=(l+r)>>1; if(judge(m))ans=m,r=m-1; else l=m+1; } printf("%d",ans); return 0; }