题目大意:
对于从1到N (1 <= N <= 39) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的。
样例输入:
7
样例输出:
4
题解:
这是个动态规划的版本,发现DP真的是个好东西。
这个题目类似于0/1背包吧,就是关于第i个数取或不取得问题。
状态方程如下:
dp[i][j]= dp[i-1][j] + dp[i-1][j-i] (j>=i);
第一个部分不取,第二个部分取
dp[i][j]= dp[i-1][j] (i>j);
这种情况只能不取;给出初始状态:
dp[1][0]=1; dp[1][1]=1;
接着就是循环了,我用了二维数组,两层循环。
C++
/*
ID:mujinui1
LANG:C++
TASK:subset
*/
#include <iostream>
#include <fstream>
#include<cstring>
using namespace std;
int main()
{
ifstream fin("subset.in");
ofstream fout("subset.out");
int n,max;
long long dp[50][1000];
fin>>n;
if(n%4==3||n%4==0){
//只有这两种情况可以分割,其他直接输出0就ok了
max=(n*(n+1))/2; //最大值
max/=2; //只用一半
memset(dp,0,sizeof(dp));
dp[1][1]=1;
dp[1][0]=1;
for(int i=2;i<=n;i++){
int sum=(i*(i+1))/2; //第二层循环上限
for(int j=0;j<=sum;j++){
if(j>=i){
dp[i][j]=dp[i-1][j]+dp[i-1][j-i];
}
else{
dp[i][j]=dp[i-1][j];
}
}
}
fout<<dp[n][max]/2<<endl;
//结果个一半才是我们所需要的,同一种情况分成两半会被重复计数
}
else{
fout<<0<<endl;
}
return 0;
}
优化的代码也出来了,空间上的优化,换成了一维数组
C++
/*
ID:mujinui1
LANG:C++
TASK:subset
*/
#include <iostream>
#include <fstream>
#include<cstring>
using namespace std;
int main()
{
ifstream fin("subset.in");
ofstream fout("subset.out");
int n,max;
long long dp[10000];
fin>>n;
if(n%4==3||n%4==0){
max=(n*(n+1))/2;
max/=2;
memset(dp,0,sizeof(dp));
dp[0]=1;
for (int i=1;i<=n;i++)
{
for (int j=max;j>=i;j--)
//这里要倒序,这样就不会出现前面的数值被覆盖掉了,
//和0/1背包优化一样的道理
dp[j]=dp[j]+dp[j-i];
}
fout<<dp[max]/2<<endl;
}
else {
fout<<0<<endl;
}
return 0;
}