HAOI2011 Problem c

本文探讨了一种使用动态规划(DP)算法解决特定场景下座位排列问题的方法。通过定义状态dp[i][j],表示在排除已指定座位的人后,第i个人之后有j个人的编号被人为确定的方案数。文章详细解释了状态转移方程,并介绍了如何预处理前缀和sum[i]以判断方案是否合法。最后给出了完整的C++代码实现。
摘要由CSDN通过智能技术生成

题目链接:戳我

\(dp[i][j]\)表示在排除掉原先已经有指定座位的人之后,第i个人之后有j个人的编号我们已经人为地确定过了 的方案数。

那么我们就有\(dp[i][j]=dp[i+1][j-k]*C_j^k\)

因为我们需要知道第i个之后有多少个人预先已经确定了座位。所以我们还需要预先处理一个前缀和sum[i],表示第i个之后预先确定好座位的人数。

什么时候不合法呢?当sum[i]>n-i+1的时候一定是不合法了。

另外备注一点,刚开始我写的时候还以为转移方程后面还要乘上一个排列。但是,仔细一想其实不然。如果i个人的位置已经确定,那么方案显然是唯一的!(因为大家都顺序向后坐嘛qwq),所以方案个数的不同,只能不同在我们之后又人为安排的序号上。(唔。。。大家理解到我想表达的是什么意思了嘛qwqwq)

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 310
using namespace std;
int t,n,m,M;
int sum[MAXN],c[MAXN][MAXN],dp[MAXN][MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&t);
    while(t--)
    {
        memset(sum,0,sizeof(sum));
        memset(dp,0,sizeof(dp));
        scanf("%d%d%d",&n,&m,&M);
        for(int i=1;i<=m;i++) 
        {
            int cur,id;
            scanf("%d%d",&cur,&id);
            sum[id]++;
        }
        for(int i=n;i>=1;i--) sum[i]+=sum[i+1];
        bool flag=true;
        for(int i=n;i>=1;i--)
            if(sum[i]>n-i+1)
            {
                printf("NO\n");
                flag=false;
                break;
            }
        if(flag==false) continue;
        /*for(int i=0;i<=10;i++)
        {
            for(int j=0;j<=i;j++)  
                printf("%d ",c[i][j]);
            cout<<endl;
        }*/
        for(int i=0;i<=n;i++) c[i][0]=c[i][i]=1;
        for(int i=2;i<=n;i++)
            for(int j=1;j<i;j++)
                c[i][j]=(c[i-1][j-1]+c[i-1][j])%M;
        dp[n+1][0]=1;
        for(int i=n;i;i--)
            for(int j=0;j<=n-sum[i]-i+1;j++)
                for(int k=0;k<=j;k++)
                    dp[i][j]=(dp[i][j]+1ll*dp[i+1][j-k]*c[j][k]%M)%M;
        printf("YES %d\n",dp[1][n-m]);
    }
    return 0;
}

转载于:https://www.cnblogs.com/fengxunling/p/10459599.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值