AmazingCounters.com

FZU Problem 2200 cleaning dp

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

 

 
 
唔,这次月赛日了狗把.A题矩阵快速幂常数卡的想日狗!
这题一开始看错题看成线性的,于是写下了这个dp方程:dp[i][j] = dp[i-3][j-1] + dp[i-4][j-2]+ dp[i-1][j] ;
dp[i][j]表示前i个人选j个人合法的方案数
 
 
嗯,怎么看都很对的样子对吧,然而第二个样例16跑不出来,花了几乎半小时才发现是环形的,然而没关系233333前面写好的代码可以不用重写,发现假设首个格子和尾格子全没放,只有首或尾放,首和尾全放一共四种情况,讨论一下就可以O(1)成线性的的了
然后边界发现好麻烦,干脆小于6的全部打表,于是写下了如此丑陋的代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define maxn 2000
#define MOD 1000000007
using namespace std;
long long dp[maxn][maxn];
int main()
{
    int t;
    int n,k;
    scanf("%d",&t);
    dp[1][1]= dp[1][0] = dp[0][0] = 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(int i=4;i<=1000;i++)
    {
        dp[i][0] = 1;
        dp[i][1] = i;
        for(int j=2;j<=(i);j++)
        {
            dp[i][j] = ((dp[i-3][j-1] + dp[i-4][j-2])%MOD + dp[i-1][j]) % MOD;
        }
    }
    int an[100][100]={0};
    an[1][0]=an[1][1]=1;
    an[2][0]=1;
    an[3][0]=1;
    an[3][1]=3;
    an[3][2]=0;
    an[4][0]=1;
    an[4][1]=4;
    an[4][2]=4;
    an[5][0]=1;
    an[5][1]=5;
    an[5][2]=5;
    an[6][0]=1;
    an[6][1]=6;
    an[6][2]=9;
    while(t--)
    {
        scanf("%d%d",&n,&k);
        long long ans=0;
        if(n<=6)
        {
            printf("%d\n",an[n][k]);
            continue;
        }
        ans = ((dp[n-2][k] + 2*(dp[n-5][k-1]+ (dp[n-6][k-2]))%MOD)%MOD+ dp[n-6][k-2])%MOD;
        //cout<<fac[k]<<" "<<fac[n-k]<<endl;
        printf("%I64d\n",ans);
    }
    return 0;
}

 

 
 
posted @ 2015-10-06 22:38  philippica  阅读(298)  评论(0编辑  收藏  举报