1. 程式人生 > >洛谷P1411 砝碼稱重

洛谷P1411 砝碼稱重

false http 時間 .org 背包 ret code 就是 傳送門

傳送門啦

這個題總體思路就是先搜索在 $ dp $

void dfs(int keep,int now){
    //使用  放棄 
    if(now > m) return;
    //已經放棄超過m個了,就退出
    if(keep == n){
        if(now == m)  dp();
        return ;
    }
    ///如果搜索完後正好符合條件,執行一次dp過程
    dfs(keep + 1 , now);
    //這個砝碼選
    vis[keep] = true;//打標記
    dfs(keep + 1 , now + 1);
    //這個砝碼放棄
    vis[keep] = false;//取消標記
}

觀察題目可得,這個過程可以通過01背包實現。

定義 $ f[i][j] $ 為當前選取到了第j個砝碼,如果通過之前的砝碼可以稱量出重量 $ i $ 那麽 $ f[i][j] $ 的值為 $ true $ 。

狀態轉移方程為: $ f[i][j]=f[i-a[i]][j-1] $

初始狀態為 $ f[0][j]=true $

最後 $ f[i][n] $ 中 $ true $ 的個數就是通過這些砝碼可以計算出的重量值。通過一維數組,我們可以只定義一個 $ f[i] $ 數組,降低了時間復雜度,註意此時內層循環倒序。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

inline int read(){
    char ch = getchar();
    int f = 1 , x = 0;
    while(ch > '9' || ch < '0'){if(ch == '-')f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + ch - '0';ch = getchar();}
    return x * f;
}

int n,m,a[300];
bool vis[300];
int ans,f[3000],tot,res; 

void dp(){
    memset(f , 0 , sizeof(f));
    f[0] = true;  ans = tot = 0;
    for(int i=0;i<n;i++){
        if(vis[i])  continue;
        for(int j=tot;j>=0;j--)
            if(f[j] && !f[j+a[i]]){
                f[j+a[i]] = true;
                ans++;
            }
        tot += a[i];
    }
    res = max(ans , res);
}

void dfs(int keep,int now){
    //使用  放棄 
    if(now > m) return;
    if(keep == n){
        if(now == m)  dp();
        return ;
    }
    dfs(keep + 1 , now); 
    vis[keep] = true;
    dfs(keep + 1 , now + 1);
    vis[keep] = false;
}

int main(){
    n = read(); m = read();
    for(int i=0;i<n;i++) {
        a[i] = read();
    }
    dfs(0 , 0);
    printf("%d\n",res);
    return 0;
}

洛谷P1411 砝碼稱重