包子湊數(完全揹包,數論)
阿新 • • 發佈:2021-08-12
裴蜀定理:若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]; intw[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; }
寫的不夠好,建議去看開頭的連結\--。--/