1. 程式人生 > 其它 >包子湊數(完全揹包,數論)

包子湊數(完全揹包,數論)

裴蜀定理:若a,b是整數,且gcd(a,b)=d,那麼對於任意的整數x,y,ax+by都一定是d的倍數,特別地,一定存在整數x,y,使ax+by=d成立。

對於此題,若d = 1,則可選的種類一定為無數種,換言,一定有有限個數不能選,求出有多少個不能選就可了。

資料範圍為10000,別問我怎麼知道的(狗頭保命)。參考原文:AcWing 1226. 包子湊數 完全揹包 ($yan氏dp + 層層分析$) - AcWing

二維dp做法:

#include <iostream>
using namespace std;

const int N = 105;
bool dp[N][10005];
int
w[N]; int n; int gcd(int a,int b) { return b ? gcd(b,a%b) : a; } int main() { ios::sync_with_stdio(false); cin>>n; int d = 0; for (int i = 1; i <= n; i++) { cin>>w[i]; d = gcd(d,w[i]); } if (d != 1) cout<<"INF\n"; else { dp[
0][0] = true; // 初始化 for (int i = 1; i <= n; i++) { // 二維完全揹包模板 for (int j = 0; j <= 10000; j++) { dp[i][j] = dp[i-1][j]; if (j >= w[i]) dp[i][j] = dp[i][j] | dp[i][j-w[i]];// 選與不選,恰是1與0,若 w[i]~j都可以選,說明j也可以湊出,為1,那不能湊出的j就是0了 } }
int ans = 0; for (int j = 0; j < 10000; j++) if (!dp[n][j]) // 不能湊出 ans++; cout<<ans<<endl; } return 0; }

一維dp做法:(補充了一個小tips)

#include <iostream> 
using namespace std;

const int N = 105;
bool dp[10005];
int n;
int w[N];

int gcd(int a,int b)
{
    return b ? gcd(b,a%b) : a; // little tips:第一組d = 0 傳過來最大公因數就是w[i] 
}

int main()
{
    ios::sync_with_stdio(false);
    
    cin>>n;
    int d = 0;
    for (int i = 1; i <= n; i++) {
        cin>>w[i];
        d = gcd(d,w[i]); 
    }
    if (d != 1) cout<<"INF\n";
    else 
    {
        dp[0]= true;
        for (int i = 1; i <= n; i++) { // 一維完全揹包模板  
            for (int j = w[i]; j < 10000; j++) {
                dp[j] |= dp[j-w[i]]; // 等同二維的解釋  
            }
        }
        int ans = 0;
        for (int j = 0; j < 10000; j++) {
            if (!dp[j]) ans++;
        }
        cout<<ans<<endl;
    }
    return 0;
}

寫的不夠好,建議去看開頭的連結\--。--/