码迷,mamicode.com
首页 > 其他好文 > 详细

HDU-3811 Permutation 状压DP

时间:2019-07-15 20:02:00      阅读:128      评论:0      收藏:0      [点我收藏+]

标签:long   printf   scanf   这一   eof   int   ati   its   统计   

题意:求满足下面条件的n的排列个数:条件为给出m个点对<x,y> 只要这个排列满足至少一个位置p[x]=y即算满足条件。

解法:这道题不算难应该要想出来的,结果没想出来自己还是蒟蒻呀qwq。我们观察满足一个p[x]=y即可以,但是这样很难统计,发现它的反面条件就是不满足任何一个p[x]=y。于是我们从这个入手用状压DP求出不满足条件数量然后用总数n!减去即是答案。

设dp[i][state]代表长度为i的排列使用数字情况state的不完美数量

用填表法DP,写出状态转移方程  dp[i+1][state|(1<<k)]+=dp[i][state] (如果state状态下i位置能填数字k)。  然后照着这个DP方程写就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,m,g[20][20];
LL fac[20],dp[1<<18];  //dp[i][state]代表长度为i的排列使用数字情况state的不完美数量 

int main()
{
    int T,Case=0; cin>>T;
    fac[0]=1; for (int i=1;i<=17;i++) fac[i]=fac[i-1]*i;
    while (T--) {
        scanf("%d%d",&n,&m);
        int ALL=(1<<n)-1;
        memset(g,0,sizeof(g));
        for (int i=1;i<=m;i++) {
            int x,y; scanf("%d%d",&x,&y);
            g[x][y]=1;  //x位置不能y 
        }
        memset(dp,0,sizeof(dp));
        dp[0]=1; LL sum=0;
        for (int i=1;i<=n;i++) {  //长度i:现在填到位置i 
            for (int j=ALL;j>=0;j--) {  //数字使用情况state 
                for (int k=1;k<=n;k++) {  //这一位置填数字k 
                    if ((1<<(k-1))&j) continue;
                    if (g[i][k]) continue;
                    dp[(1<<(k-1))|j]+=dp[j];
                }
                dp[j]=0;
            }
        }
        for (int i=0;i<=ALL;i++) sum+=dp[i];
        printf("Case %d: %lld\n",++Case,fac[n]-sum);
    }
    return 0;
}

 

HDU-3811 Permutation 状压DP

标签:long   printf   scanf   这一   eof   int   ati   its   统计   

原文地址:https://www.cnblogs.com/clno1/p/11190981.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!