【P1025 [NOIP2001 提高組] 數的劃分】題解
阿新 • • 發佈:2021-12-18
題目連結
題目
將整數 \(n\) 分成 \(k\) 份,且每份不能為空,任意兩個方案不相同(不考慮順序)。
例如:\(n=7\),\(k=3\),下面三種分法被認為是相同的。
\(1,1,5\);
\(1,5,1\);
\(5,1,1\).
問有多少種不同的分法。
思路
首先我們可以打出一個暴力。然而為了防止重複,我們可以規定每次枚舉出的這個數要大於等於上一個數。
然後只有40分。
考慮剪枝。
- 建設剩下的 \(n\) 要分成 \(k\) 份,每份大小至少為 \(v\)。那麼如果 \(n\leqslant k\times v\),剩下的必然不夠分。
- 然後我們發現好像還超時,於是我們嘗試規定每次列舉的數都要大於
- 然後這樣只有80分,我們嘗試第1點中放到列舉過程裡,然後就可以ac了。
總結
對於搜尋剪枝的題目,我們可以超時考慮列舉的上下界。當還不行的時候,我們就把上下界放到列舉過程中,這能使程式執行效率大大增加。
Code
// Problem: P1025 [NOIP2001 提高組] 數的劃分 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P1025 // Memory Limit: 125 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include<bits/stdc++.h> using namespace std; #define int long long inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+ (x<<3)+(ch^48);ch=getchar();}return x*f;} //#define mo //#define N //#define M int n, m, i, j, k; int f[210][10][10]; int dfs(int n, int k, int v) { if(n==k&&k==0) return 1; if(k==0&&n) return 0; if(n<v*k) return 0; // if(f[n][k][v]!=-1) return f[n][k][v]; int ans=0; for(int i=v; i*k<=n; ++i) for(int j=1; j*i<=n&&j<=k; ++j) ans+=dfs(n-i*j, k-j, i+1); // return f[n][k][v]=ans; return ans; } signed main() { // freopen("tiaoshi.in","r",stdin); // freopen("tiaoshi.out","w",stdout); memset(f, -1, sizeof(f)); n=read(); k=read(); printf("%lld", dfs(n, k, 1)); return 0; }