晓萌希望将 11 到 NN 的连续整数组成的集合划分成两个子集合,且保证每个集合的数字和是相等。例如,对于 N=3,对应的集合{1,2,3} 能被划分成{3} 和 {1,2} 两个子集合.
这两个子集合中元素分别的和是相等的。
对于N=3 ,我们只有一种划分方法,而对于 N=7 时,我们将有 4 种划分的方案。
输入格式
输入包括一行,仅一个整数,表示 N(1≤N≤39)的值。
输出格式
输出包括一行,仅一个整数,晓萌可以划分对应 N 的集合的方案的个数。当没法划分时,输出 0。
样例输入
7
样例输出
4
最近玩递归玩的有点上瘾,看到这个题,第一想法就是递归,简单粗暴,然后果断TLE。。。无奈之下,试试刚刚get的左神的大套路,居然特么,,,就这样把递归改成动规过了。。。
递归代码如下:
void fun(int i,int sum){
if(sum==aim/2){
cnt++;
return;
}
if(sum>aim/2) return ;
for(int j = i+1;j < n;j++){
fun(j,sum+j);
}
}
1,考虑动规维度: 由于变量是两个,那么动规的表当然就是二维的啦。
2,动规初始值:动规的初始值就是递归的出口,对于本题就是dp[i][aim/2]=1;
3,解得位置:递归第一次传的值是啥,解的位置就是啥啦,递归用的fun(0,0),解就是dp[0][0]了,最后注意本题重复计算了,需要除以2.
4,转移方程:动规的核心,这特么的会了,code就轻松加愉快了。动规的转移方程就是递归的递归体,不过需要稍微修改一下。比如说递归求y需要x,那么动规则是知道x求y,逆向思维一下,转移方程就get了。
附上完整代码:
#include<bits/stdc++.h>
using namespace std;
int cnt=0,n,aim=0;
long long dp[40][800]={0};
int main(){
cin>>n;
for(int i=1;i<=n;i++){
aim+=i;
}
if(aim%2)cout<<"0"<<endl;
else{
for(int i=1;i<=n;i++){
dp[i][aim/2] = 1;
}
for(int i=aim/2;i>=0;i--){
for(int j=0;j<=n;j++){
for(int k=j+1;k<=n;k++){
if(i>=k)
dp[j][i-k] += dp[k][i];
}
}
}
cout<<dp[0][0]/2<<endl;
}
return 0;
}