1. 程式人生 > >洛谷P1441 砝碼稱重(加強版)

洛谷P1441 砝碼稱重(加強版)

題目連結

P1441 砝碼稱重

解題思路:

搜尋/列舉+dp
dp過程參考弱化版的P2347 砝碼稱重
妙啊,真的是妙啊…
感覺這題搜尋和dp結合的恰到好處。利用dfs先枚舉出所有可能的情況,當陣列 a [ i ] a[i]

i]中被標記的數值達到 m m 後,表示已經捨棄了 m m 個值,在剩下的 n
m n-m
個值裡面dp就行了。
在dp過程,由於題目資料範圍可知,所有砝碼的和一定不超過2000,所以用一個數組 f [ i ] f[i]
表示 n m n-m 個砝碼組合得到的和可以是 i i ,然後統計 f [ i ] f[i] 裡面不為0的個數就可以了。
最後注意在更新 f [ i ] f[i] 的過程中,逆序更新,防止之前更新的值影響已經更新過的值。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>//memset 
using namespace std;
int n,m;
int a[30]; 
int vis[30],now=1;
int f[2010];//f[0]=1; 
int ans=0;
void dp(){
    memset(f,0,sizeof(f));
    f[0]=1;
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            for(int j=2000;j>=a[i];j--){
                if(f[j-a[i]]&&!f[j])
                    f[j]=1;
            }
        }
    }
    int cnt=0;
    for(int i=1;i<=2000;i++){
        if(f[i]) cnt++;
    }
    ans=max(ans,cnt);
}
void dfs(int k){
    if(k==m) {
        dp();
        return;
    }
    for(int i=now;i<=n;i++){
        if(!vis[i]){
            vis[i]=1;
            now=i+1;
            dfs(k+1);//計數,列舉 
            vis[i]=0;
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    sort(a+1,a+n+1);
    dfs(0);
    printf("%d\n",ans);
    return 0;
} 
參考思路:

https://www.luogu.org/blog/yeyangrui/solution-p1441