【dfs或者dp】等和的分隔子集
阿新 • • 發佈:2018-12-30
這道題我首先的思路是dfs,確實可以做,但是N上了30之後就執行超時了。說明dfs還是蠻容易超時的,適合小資料。
#include<iostream> #include<bits/stdc++.h> using namespace std; long long allsum=0; int cnt=0; int N; void dfs(int i,long long sum) { if(sum==allsum/2) { cnt++; return; } if(sum>allsum/2) return; for(int j=i-1;j>=1;j--) { sum+=j; dfs(j,sum); sum-=j; } } int main() { cin>>N; for(int i=1;i<=N;i++) allsum+=i; if(allsum%2!=0) { cout<<0; return 0; } dfs(N,N); cout<<cnt; return 0; }
後來去網上看了看其他人的思路,發現媽耶,可以用dp做。
因為這道題其實也可以類似成“裝東西”的題,所以很像01揹包的感覺。因此,重點是要找到dp陣列下標和數值含義以及狀態轉移方程。
神奇的思路在於,當dp[j]下標j表示當前子集的和為j,數值代表有多少個和為j的子集,那麼dp[j]=dp[j]+dp[j-i] ,這個i就是當前我應該放入的數。
分析:每個數字只用到一次,每種情況的存在數可以由之前的存在的數來遞推得到,01揹包的變形。
#include<iostream> #include<bits/stdc++.h> using namespace std; int allsum=0; int cnt=0; int N; long long dp[10000]; int main() { memset(dp,0,sizeof(dp)); cin>>N; for(int i=1;i<=N;i++) allsum+=i; if(allsum%2!=0) { cout<<0; return 0; } dp[0]=1; //!!!!空集也是集合!,重點是下面的迴圈裡當j=i時,dp[i]+=dp[0]的含義就是一個子集只要i一個就行,所以算一個方案數 加上。 for(int i=1;i<=N;i++) //向背包中(一定)放入i { for(int j=allsum/2; j>=i; j--) { dp[j]+=dp[j-i]; } } cout<<dp[allsum/2]/2; return 0; }