洛谷P1386座位安排

座位安排
今天,在机房里做了这道题目,我来整理一下思路。
首先读懂题意,这n个人是不需要按1到n来一次安排的,也就是说你可以先安排任意一个人。
那么有一种很好排除的情况,那就是对于大于等于i的作为的需求量s[i]是不得超过n-i+1的,这个很好理解。
那么这个s[i]我们可以在读入内定的几个位置时,用一个叫use[i]的数组去处理,它表示内定为i的人有多少个,那么s[]也就出来了。
接下来我们需要预处理一下组合数,以后需要。
下面我们看一下核心,这道题目我们用的时dp
我们用f[i][j]表示对于大于等于i的位置在有j个已经确定的方案数,注意这里的确定,不是指内定的人数,而是指除了内定的人数之外,却定j个人,有多少种方案。
那么我们再来写转移方程。
这个应该就很好写了:
f[i][j]=∑k=0j(f[i+1][j−k]∗c[j][k])
也就是说当前确定j个人等于i+1位置之前的0到j个确定了的种数乘以对应的组合数。
什么意思?就是指如果从前面确定的j个人选出k个人不确定,有c[j][k]种选法。为什么要求和?这选出的k个人就相当于,从当前这个i位置放入。
所以这就是结果了。
下面贴代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=305;
int T,n,m,M;
bool flag;
long long use[maxn],s[maxn],c[maxn][maxn],f[maxn][maxn];
int main(){
    scanf("%d",&T);
    while(T--){
        memset(use,0,sizeof(use));
        memset(s,0,sizeof(s));
        memset(c,0,sizeof(c));
        memset(f,0,sizeof(f));
        scanf("%d%d%d",&n,&m,&M);
        flag=true;
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            use[y]++;
        }
        for(int i=n;i>=1;i--){
            s[i]=s[i+1]+use[i];
            if(s[i]>n-i+1){
                flag=false;
                break;
            }
        }
        if(!flag){
            printf("NO\n");
            continue;
        }
        for(int i=0;i<=n;i++){
            c[i][0]=c[i][i]=1;
            for(int j=1;j<i;j++){
                c[i][j]=(c[i-1][j-1]+c[i-1][j])%M;
            }
        }
        f[n+1][0]=1;
        for(int i=n;i>=1;i--){
            for(int j=0;j<=n-i+1-s[i];j++){
                for(int k=0;k<=j;k++){
                    f[i][j]=(f[i][j]+f[i+1][j-k]*c[j][k])%M;
                }
            }
        }
        printf("YES %d\n",f[1][n-m]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cggwz/article/details/80513600
今日推荐