HLG 1259 HaHa's Morning【状态压缩DP】

题意: HAHA 早上有 n 件事情要做, 知道了有 m 个关系,每个关系 a ,b 表示a 事情必须在b 事情之前做,问做完这些事共有多少种可能的顺序。

分析: 可以把题目看成是 一个有向图,其中共有多少种拓扑排序的方法。

          首先图中不能有环,可以先传递闭包,看看有没有 g[i][i]=1 的情况出现,如果有则说明有环。

          由于 n <=17 ,1<<17 相对较小,可以枚举所有状态进行递推。

① 递归写法

#include<stdio.h>
#include<string.h>
#define clr(x)memset(x,0,sizeof(x))
int g[18][18];
long long dp[1<<18];
int n;
long long c_c(int s)
{
    long long res=dp[s];
    if(res>=0)
        return res;
    int i,j;
    for(i=0;i<n;i++)        // 判断是否有冲突的情况
        if(s&(1<<i))
            for(j=0;j<n;j++)
                if(g[j][i]&&!(s&(1<<j)))
                    return dp[s]=0;
    dp[s]=0;
    for(j=s;j;j^=i)
    {
        i=j&-j;
        dp[s]+=c_c(s^i);
    }
    return dp[s];
}
int main()
{
    int flag,m,a,b,i,j,k,st,stat;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        flag=0;
        clr(g);
        while(m--)
        {
            scanf("%d%d",&a,&b);
            g[a-1][b-1]=1;
        }
        for(k=0;k<n;k++)
            for(i=0;i<n;i++)
                if(g[i][k])
                for(j=0;j<n;j++)
                    g[i][j]=g[i][j]||(g[i][k]&&g[k][j]);
        // 是否有环
        for(i=0;i<n;i++)
            if(g[i][i])
                break;
        if(i<n)
        {
            printf("0\n");
            continue;
        }
        st=(1<<n)-1;  // 总状态数
        memset(dp,0xff,sizeof(dp));
        dp[0]=1;
        printf("%lld\n",c_c(st));
    }
    return 0;
}

 

posted @ 2012-08-04 14:45  'wind  阅读(234)  评论(0编辑  收藏  举报