1. 程式人生 > >【codevs1297】硬幣 完全揹包

【codevs1297】硬幣 完全揹包

題目大意:給定 N 種不同種類的硬幣,每種硬幣的重量範圍在一個可變區間內,但是價值恆定,求給定一個重量 W,求有多少種面值不同的組合方式。

題解:如果硬幣的重量恆定,那麼就是一道裸的完全揹包問題。因此,可以先將給定的硬幣拆分成多個重量不同的硬幣。

總的來說,這道題所求的是目標狀態有多少種可能的解,而不是最優解,因此有以下兩種方式。

解法1:在每個狀態中維護一個 \(STL--set\),用來儲存到達該狀態所有可能的值,最後輸出集合的大小即可,常數較大。
解法2:將問題轉化為判定性問題,即:額外增加一維用來表示當前可能的價值。

程式碼 1 如下

#include <bits/stdc++.h>
using namespace std;

int w,n,ans,val[300],cost[300],tot;
set<int> dp[110];

void read_and_parse(){
    scanf("%d%d",&w,&n);
    for(int i=1,v,mi,mx;i<=n;i++){
        scanf("%d%d%d",&v,&mi,&mx);
        for(int j=mi;j<=mx;j++)cost[++tot]=j,val[tot]=v;
    }
}

void solve(){
    dp[0].insert(0);
    for(int i=1;i<=tot;i++)
        for(int j=cost[i];j<=w;j++)
            for(set<int>::iterator p=dp[j-cost[i]].begin();p!=dp[j-cost[i]].end();p++)
                dp[j].insert(*p+val[i]);
    printf("%d\n",dp[w].size());
}

int main(){
    read_and_parse();
    solve();
    return 0;   
}

程式碼 2 如下

#include <bits/stdc++.h>
using namespace std;

int w,n,ans,val[300],cost[300],tot,dp[110][2510];

void read_and_parse(){
    scanf("%d%d",&w,&n);
    for(int i=1,v,mi,mx;i<=n;i++){
        scanf("%d%d%d",&v,&mi,&mx);
        for(int j=mi;j<=mx;j++)cost[++tot]=j,val[tot]=v;
    }
}

void solve(){
    dp[0][0]=1;
    for(int i=1;i<=tot;i++)
        for(int j=cost[i];j<=w;j++)
            for(int k=val[i];k<=2500;k++)
                dp[j][k]|=dp[j-cost[i]][k-val[i]];
    for(int i=0;i<=2500;i++)if(dp[w][i])++ans;
    printf("%d\n",ans);
}

int main(){
    read_and_parse();
    solve();
    return 0;   
}