【計蒜客系列】挑戰難題28:等和的分隔子集
阿新 • • 發佈:2019-01-10
曉萌希望將1到N的連續整陣列成的集合劃分成兩個子集合,且保證每個集合的數字和是相等。例如,對於N=3,對應的集合{1,2,3}能被劃分成{3} 和 {1,2}兩個子集合.
這兩個子集合中元素分別的和是相等的。
對於N=3,我們只有一種劃分方法,而對於N=7時,我們將有4種劃分的方案。
輸入包括一行,僅一個整數,表示N的值(1≤N≤39)。
輸出包括一行,僅一個整數,曉萌可以劃分對應N的集合的方案的個數。當沒發劃分時,輸出0。
樣例1
輸入:
7
輸出:
4
注:動態規劃
dp[i][j] 表示在區間[1,i]之間,和為j的子集的個數。狀態轉移方程如下:
(1) dp[i][j] = dp[i-1][j] ; (j < i)
(2) dp[i][j] = dp[i-1][j] + 1 ; (j == i)
(3) dp[i][j] = dp[i-1][j] + dp[i-1][j-i] ; (j > i)
#include<iostream> using namespace std; long long dp[40][40 * (40 + 1) / 2]; int main() { int n; cin >> n; int half = n * (n + 1) / 4;//集合一半的和 if (n * (n + 1) % 4 != 0) { cout << 0 << endl; return 0; } dp[1][1] = 1; for (int i = 2; i <= n; i++) for (int j = 1; j <= half; j++) if (j < i) dp[i][j] = dp[i - 1][j]; else if (j == i) dp[i][j] = dp[i - 1][j] + 1; else dp[i][j] = dp[i - 1][j] + dp[i - 1][j - i]; cout << dp[n][half] / 2 << endl; return 0; }
優化:
#include<iostream> using namespace std; const int maxn = 10000; long long f[100000]; int n, s; int main() { cin >> n; s = n * (n + 1); //1~n的和的2倍 if (s % 4 != 0) { //不能進行劃分 cout << 0 << endl; return 0; } s /= 4; f[0] = 1; for (int i = 1; i <= n; i++) for (int j = s; j >= i; j--) f[j] += f[j - i]; cout << f[s] / 2 << endl; return 0; }
列舉:
(此法超時,只供理解)
#include<iostream>
#include<cstring>
using namespace std;
int main() {
int n;
cin >> n;
int *Pos = new int[n];
int *Array = new int[n + 1];
for (int i = 0; i < n; i++)
Array[i] = i + 1;
for (int i = 0; i < n; i++)//當前陣列中值
cout << Array[i] << " ";
cout << endl;
int top = 0;
int m = n * (n + 1) / 4; //陣列和的一半
int i, j;
int count = 0;//滿足條件子集和的個數
i = 0;
memset(Pos, 0, sizeof(Pos));
while (i <= n && top >= 0) {
while (m > 0 && i < n) {
if (Array[i] <= m) {
Pos[top++] = i;
m -= Array[i];
}
i++;
}
if (m == 0) {
for (j = 0; j < top; j++) {
cout <<Array[Pos[j]]<<" ";
}
cout << endl;
count++;
}
i = Pos[--top];
if (top < 0)
break;
Pos[top] = 0;
m += Array[i];
i++;
}
cout<< count / 2 <<endl;
}