hdu2639

 解決了2602之後,這道題跟上面的題目有些不同,因爲這裏要求的是第K優解

 

 

 

求第K優解

 

對於求次優解、第K優解類的問題,如果相應的最優解問題能寫出狀態轉移方程、用動態規劃解決,那麼求次優解往往可以相同的複雜度解決,第K優解則比求最優解的複雜度上多一個係數K。其基本思想是將每個狀態都表示成有序隊列,將狀態轉移方程中的max/min轉化成有序隊列的合併。這裏仍然以01揹包爲例講解一下。首先看01揹包求最優解的狀態轉移方程:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}。如果要求第K優解,那麼狀態f[i][v]就應該是一個大小爲K的數組f[i][v][1..K]。其中f[i][v][k]表示前i個物品、揹包大小爲 v時,第k優解的值。“f[i][v]是一個大小爲K的數組”這一句,熟悉C語言的同學可能比較好理解,或者也可以簡單地理解爲在原來的方程中加了一維。顯然f[i][v][1..K]這K個數是由大到小排列的,所以我們把它認爲是一個有序隊列。然後原方程就可以解釋爲:f[i][v]這個有序隊列是由f[i-1][v]和f[i-1][v-c[i]]+w[i]這兩個有序隊列合併得到的。有序隊列f[i-1][v]即f[i-1][v][1..K],f[i-1][v-c[i]]+w[i]則理解爲在f[i-1][v-c[i]] [1..K]的每個數上加上w[i]後得到的有序隊列。合併這兩個有序隊列並將結果的前K項儲存到f[i][v][1..K]中的複雜度是O(K)。最後的答案是f[N][V][K]。總的複雜度是O(VNK)。

 

爲什麼這個方法正確呢?實際上,一個正確的狀態轉移方程的求解過程遍歷了所有可用的策略,也就覆蓋了問題的所有方案。只不過由於是求最優解,所以其它在任何一個策略上達不到最優的方案都被忽略了。如果把每個狀態表示成一個大小爲K的數組,並在這個數組中有序的保存該狀態可取到的前K個最優值。那麼,對於任兩個狀態的max運算等價於兩個由大到小的有序隊列的合併。另外還要注意題目對於“第K優解”的定義,將策略不同但權值相同的兩個方案是看作同一個解還是不同的解。如果是前者,則維護有序隊列時要保證隊列裏的數沒有重複的。

 

 

題目:

http://acm.hdu.edu.cn/showproblem.php?pid=2639

 

可以先看看下面的資料再看代碼。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
int f[1005][35];
int c[105],w[105];


int main()
{
    int T,N,V,i,j,K;
    int kk,a,b,ci;
    scanf("%d",&T);
    while(T--)
    {
        int A[35],B[35];
        memset(f,0,sizeof(f));
    //    memset(c,0,sizeof(c));
    //    memset(w,0,sizeof(w));
        scanf("%d%d%d",&N,&V,&K);
        for(i=1;i<=N;i++)
            scanf("%d",&w[i]);
        for(j=1;j<=N;j++)
            scanf("%d",&c[j]);
        for(i=1;i<=N;i++)
        {
            for(j=V;j>=c[i];j--)
            {
                for(kk=1;kk<=K;kk++)
                {
                    A[kk]=f[j-c[i]][kk]+w[i];
                    B[kk]=f[j][kk];
                }
               
                A[kk]=-1,B[kk]=-1;//結束循環的一個標誌
                a=b=ci=1;
                while( ci<=K && ( A[a]!=-1 || B[b]!=-1))
                {
                    if (A[a]>B[b])
                    {
                        f[j][ci]=A[a];
                        a++;
                    }
                    else
                    {
                        f[j][ci]=B[b];
                        b++;
                    }
                    if(f[j][ci]!=f[j][ci-1]) ci++; //這裏是爲了防止相同的數也佔用
                                                 //不同的名次,比如f[10]有可能有
                                               //幾個相同的結果但它們只能有一個名次而已
                }
            }
        }
        printf("%d\n",f[V][K]);
    }
    return 0;
}


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