foj2200 Problem 2200 cleaning dp

32 篇文章 0 订阅

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值