动态规划练习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;
}

 

posted @ 2017-07-25 14:15  kakakakakaka  阅读(214)  评论(0编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效