题目链接:
http://acm.uestc.edu.cn/#/problem/show/1307
ABCDE
Memory Limit: 262144/262144KB (Java/Others)
样例输出
1
1
2
2
4
题意
求子集能够表示1、2、...、n所有数的集合种数。
比如n=5:{1,1,1,1,1},{1,1,1,2},{1,2,2},{1,1,3}总共4种
题解
dp[i][j]表示和为i,且集合里面最大的数为j的总数,则有dp[i][j]=sigma(dp[i-j][k])其中k<=j,j*2-1<=i。 当然这样转移会n3超时,不过我们可以处理出sumv[i][j]=sig(dp[i][k])其中k<=j。然后O(n2)就能跑。
为什么j*2-1<=i?假设我们已知和为s且满足条件的集合数,那么我们考虑再加一个数x,那么这个数肯定要<=s+1,否则s+1就会无法表示!也就是说如果j能够属于和为i的集合里面的最大数,那么必然就有j+j-1<=i。
代码
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define lson (o<<1)
#define rson ((o<<1)|1)
#define mid (l+(r-l)/2)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=a;i<(b);i++)
#define scf scanf
#define prf printf
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;
const int INF=0x3f3f3f3f;
const LL INFL=0x3f3f3f3f3f3f3f3fLL;
const double eps=1e-8;
const double PI = acos(-1.0);
//start----------------------------------------------------------------------
const int maxn=5001;
const int mod=1e9+7;
LL dp[maxn][maxn];
void pre(){
clr(dp,0);
dp[1][1]=1;
for(int j=1;j<maxn;j++) dp[1][j]=(dp[1][j-1]+dp[1][j])%mod;
for(int i=2;i<maxn;i++){
for(int j=1;j*2-1<=i;j++)
dp[i][j]=dp[i-j][j];//这里的dp[i-j][j]已经变成是sum[i-j][j]了,但是由于空间比较紧,就没另开
//预处理出前缀和。
for(int j=1;j<maxn;j++)
dp[i][j]=(dp[i][j-1]+dp[i][j])%mod;
}
}
int main() {
pre();
int tc,kase=0;
scanf("%d",&tc);
while(tc--){
int x; scf("%d",&x);
prf("%lld
",dp[x][5000]);
}
return 0;
}
//end-----------------------------------------------------------------------