PAT甲題題解-1068. Find More Coins (30)-dp,01背包


一開始沒多想,雖然注意到數據N<=10^4的范圍,想PAT的應該不會超時吧,就理所當然地用dfs做了,結果最后一組真的超時了。
剪枝啥的還是過不了,就意識到肯定不是用dfs做了。
直到看到別人說用01背包的思路,果真好久沒做題了智力水平下降,且原本dp就是我的弱項,壓根就沒把這題往dp上去想額。。。

(http://www.liuchuo.net/archives/2323)

 

題意:從n個硬皮中選取方案,使得總和價值正好為m,如果有多種,方案為排列最小的那個。

可以把硬幣看成w=v(即容量=價值)的物品,現在要選取這些物品放入到容量為m的背包中,求能裝的最大價值。
如果最大價值恰好等於容量m,那么方案則是可行的,否則輸出No Solution。
由於要輸出排列最小的方案,所以先將硬幣按價值從大到小排列,相當於我先裝大的,再裝小的。
接着用01背包的方法求dp[j]。
chosen[i][j]數組表示背包里容量為j的時候,是否含有第i個物品,用於最后序列的輸出。

 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
#include <vector>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=10000+4;
const int maxv=105;
int coins[maxn];
int dp[maxv]; //dp[v]表示:總和加起來=v的序列中最后一個值最小為多少。
int chosen[maxn][maxv];
int ans[maxn];
int cnt=0;

bool cmp(int a,int b){
    return a>b;
}
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%d",&coins[i]);
    }
    sort(coins,coins+n,cmp);

    dp[0]=0;
    for(int i=0;i<n;i++){
        for(int j=m;j>=coins[i];j--){
                if(dp[j]<=dp[j-coins[i]]+coins[i]){
                    dp[j]=dp[j-coins[i]]+coins[i];
//printf("i:%d j:%d dp:%d chosen:%d\n",i,j,dp[j],coins[i]);
                    chosen[i][j]=1;
                }
        }
    }
    if(dp[m]!=m){
        printf("No Solution");
    }
    else{
        int v=m,idx=n-1;
        while(v){
            if(chosen[idx][v]){
                ans[cnt++]=coins[idx];
                v-=coins[idx];
            }
            idx--;
        }
        printf("%d",ans[0]);
        for(int i=1;i<cnt;i++)
            printf(" %d",ans[i]);
    }
    return 0;
}
View Code

 

PS:想了解背包問題的,有個《背包九講》,網上可以搜搜看看,挺好的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM