zoj 3211 砍樹 有順序的dp

zoj 3211

有n棵樹,m天,每天只能砍一棵樹,每棵樹的初始價值爲ai,每過一天價值增長bi,求m天砍樹得到的最大價值。

開始貪心不可以,n棵樹長m天好像可以分成n*m棵樹再分組揹包(一棵樹只能砍一次)(裝酒問題),dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+w[i]),但是一天有隻能砍一棵樹,有兩個限制條件,直接揹包有後效性,(放第i棵樹可能改變dp[i-1][j]的值,即前面的狀態不能保證是最終的狀態)        不行。

n棵樹,可以砍m天,就是n棵樹中砍了m棵,那麼應如何安排砍這m棵樹的先後呢?顯然m棵全砍掉,a1……am全部得到,考慮價值的增量,要按b的升序依次來砍才能使總和最大,這是高中數學不等式選講裏的一個什麼不等式,大數乘大數相應的和最大。先將樹按照b來升序排列,這樣來保證每一組dp[i][j]中的j棵樹的價值都是按照天數*b遞增的來計算,當前第i棵樹的b最大,也就不可能再放在j天前了,這樣每次的dp[i-1][j]就是最優的結果,既然每個j棵樹的順序能確定,接下來就是在n棵樹裏選m棵,再對樹進行分組揹包就沒有問題了。

方程dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+w[i]),i j升序,dp[i][j]表示在前i棵樹中j天的最大價值也就是i棵樹中砍j棵

和hdu 3466類似,驕傲的商人那個,都是有帶順序的揹包,要考慮揹包問題的本質

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int dp[300][300];
struct node{
    int a,b;
}s[300];
int cmp(const void*aa,const void*bb)
{
    node*a=(node*)aa;
    node*b=(node*)bb;
    return a->b > b->b;
}
int max(int a,int b)
{
    return a>b?a:b;
}
int n,m;
int main()
{
    int i,j,k,l;
    scanf("%d",&l);
    while(l--)
    {
        scanf("%d %d",&n,&m);
        for(i=1;i<=n;i++)
            scanf("%d",&s[i].a);
        for(i=1;i<=n;i++)
            scanf("%d",&s[i].b);
        qsort(s+1,n,sizeof(s[0]),cmp);
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
            {
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+s[i].a+s[i].b*(j-1));
            }
        }
        printf("%d\n",dp[n][m]);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章