Problem 2200 cleaning
Accept: 36 Submit: 56
Time Limit: 1000 mSec Memory Limit : 65536 KB
Problem Description
N个人围成一圈在讨论大扫除的事情,需要选出K个人。但是每个人与他距离为2的人存在矛盾,所以这K个人中任意两个人的距离不能为2,他们想知道共有多少种方法。
Input
第一行包含一个数T(T<=100),表示测试数据的个数。
接下来每行有两个数N,K,N表示人数,K表示需要的人数(1<=N<=1000,1<=K<=N)。
Output
输出满足题意的方案数,方案数很大,所以请输出方案数mod 1,000,000,007 后的结果。
Sample Input
2
4 2
8 3
Sample Output
4
16
Source
FOJ有奖月赛-2015年10月
Submit Back Status Discuss
首先化环成线,先考虑没有环的情况,dp[i][j]表示,i个人选 j个,没有两人相距为2的总方案数。则可以推出。
dp[i][j] = dp[i-1][j] + dp[i-3][j-1]+ dp[i-4][j-2];
也就是第一个元素不选转化为dp[i-1][j];
第一个人选,第二个人也选
第一个人选,第二个人不选。
分别对应以上三种情况。
怎么样化环成线呢,由于,距离最大为2,故,如果判明了首尾两人的情况,剩下的就成了线的情况了。
分为4种情况,首尾都不选,选其一,都选。对应的dp方程
ans[i][j] = dp[i-6][j - 2]) + dp[i-5][j-1] * 2 + dp[i-6][j-2] * 2 + dp[i-2][j];
初始化的时候有些麻烦,因为,在边界处,有特殊的情况,只能一个个的初始化了。总复杂度为o(n * n );打表后,O(1)查询。
#define N 1005
#define M 100005
#define maxn 205
#define MOD 1000000007
int n,dp[N][N],T,k,ans[N][N];
int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
fill(dp,0);
dp[0][0] = 1;
dp[1][0] = 1;
dp[1][1] = 1;
dp[2][0] = 1;
dp[2][1] = 2;
dp[2][2] = 1;
dp[3][0] = 1;
dp[3][1] = 3;
dp[3][2] = 2;
For(i,4,N){
For(j,0,i+1){
if(i >= 1){
dp[i][j] = (dp[i][j] + dp[i-1][j]) % mod;
}
if(i>=3 && j>=1){
dp[i][j] = (dp[i][j] + dp[i-3][j-1]) % mod;
}
if(i>=4 && j>=2){
dp[i][j] = (dp[i][j] + dp[i-4][j-2]) % mod;
}
}
}
fill(ans,0);
ans[0][0] = ans[1][0] = ans[2][0] = ans[3][0] = ans[4][0] = ans[5][0] = 1;
ans[1][1] = 1;
ans[2][1] = 2;
ans[3][1] = 3;
ans[4][1] = 4;
ans[5][1] = 5;
ans[2][2] = 1;
ans[4][2] = 4;
ans[5][2] = 5;
For(i,6,N){
For(j,0,N){
if(i >= 6 && j >= 2){
ans[i][j] = (ans[i][j] + dp[i-6][j - 2]) % mod;
}
if(i>=5 && j>=1){
ans[i][j] = (ans[i][j] + dp[i-5][j-1] * 2 % mod) % mod;
}
if(i>=6 && j>=2){
ans[i][j] = (ans[i][j] + dp[i-6][j-2] * 2 % mod) % mod;
}
if(i>=2){
ans[i][j] = (ans[i][j] + dp[i-2][j]) % mod;
}
}
}
while(S(T)!=EOF)
{
while(T--){
S2(n,k);
printf("%d\n",ans[n][k]);
}
}
//fclose(stdin);
//fclose(stdout);
return 0;
}