【ssoj1027】树形图计数

来源:互联网 发布:c 语言英文怎么说 编辑:程序博客网 时间:2024/05/17 08:08

2407: 树形图计数

时间限制: 1 Sec  内存限制: 128 MB

题目描述

小 k 同学最近正在研究最小树形图问题。所谓树形图,是指有向图的一棵有根的生成树,其中树的每一条边的指向恰好都是从根指向叶结点的方向。现在小 k在纸上画了一个图,他想让你帮忙数一下这个图有多少棵树形图

输入

第1行输入 1个正整数n,表示图中点的个数 
第2到第n+1行每行输入n个字符,描述了这个图的邻接矩阵。 
第i+1行第j个字符如果是0则表示没有从i连向j的有向边,1表示有一条从i到j的有向边。

输出

输出1行1个整数,表示这个有向图的树形图个数

样例输入

40100001000011000

样例输出

4

提示 对于100%的数据,n≤ 8.


题意就是给你若干条有向边,求有多少种不同的生成树。

因为题目数据规模太水,所以可以直接暴力,即枚举树上每一个点的父亲,判环可以用并查集暴力。

但因为根节点没有父亲,所以需要提前枚举。

#include<cstdio>#include<iostream>#include<algorithm>#include<cmath>#include<cstring>using namespace std;#define MAXN 8#define MAXM#define INF 0x3f3f3f3ftypedef long long int LL;bool G[MAXN+5][MAXN+5];int N;int fa[MAXN+5];int root(int x){return fa[x]==x?x:root(fa[x]);}int dfs(int &rt,int u)//此处是以rt为根,枚举u和谁相连(其中树中边从叶节点向根指){    if(rt==u)return dfs(rt,u+1);    if(u>N)return 1;    int rn=0;    for(int v=1;v<=N;++v)        if(G[v][u]&&root(v)!=root(u))        {            fa[u]=v;            rn+=dfs(rt,u+1);            fa[u]=u;        }    return rn;}int main(){    scanf("%d",&N);    int i,j;    char s[MAXN+5];    for(i=1;i<=N;++i)    {        scanf("%s",s+1);        for(j=1;j<=N;++j)            G[i][j]=s[j]-'0';        fa[i]=i;    }    int ans=0;    for(i=1;i<=N;++i)        ans+=dfs(i,1);    printf("%d\n",ans);}

除此之外,这道题还可以用状压DP解决。
dp[i][s]表示以i为根,集合中状态为s的构造方案数。
dp[i][s]=dp[i][s0]*dp[j][s^s0]*连边的方案数
但因为dp[i][s]会在转移中重复计算 节点数-1 次,(同一种连边方式可能由不同的节点作为根转移过来)
所以算出dp[i][s]后要除以 节点数-1 。

#include<cstdio>#include<iostream>#include<algorithm>#include<cmath>#include<cstring>using namespace std;#define MAXN 8#define MAXS 256#define INF 0x3f3f3f3ftypedef long long int LL;bool G[MAXN+5][MAXN+5];int N;int dp[MAXN+10][MAXS+100];int main(){    scanf("%d",&N);    int i,j;    char str[MAXN+5];    for(i=1;i<=N;++i)    {        scanf("%s",str+1);        for(j=1;j<=N;++j)            G[i][j]=str[j]-'0';    }    for(i=1;i<=N;++i)        dp[i][1<<(i-1)]=1;    int M=(1<<N)-1;    for(int s=1;s<=M;++s)        for(i=1;i<=N;++i)if(s&(1<<(i-1)))        {            for(int s0=1;s0<s;++s0)if((s0|s)==s)                for(j=1;j<=N;++j)if(i!=j&&((1<<(j-1))&(s^s0)))                {                    int cnt=0;                    for(int k=1;k<=N;++k)                        if(G[k][j]&&(s0&(1<<(k-1))))++cnt;                    dp[i][s]+=cnt*dp[i][s0]*dp[j][s^s0];                }            int cnt=0;            for(j=1;j<=N;j++)                if(s&(1<<(j-1)))++cnt;            if(cnt-1)dp[i][s]/=cnt-1;        }    int ans=0;    for(i=1;i<=N;++i)        ans+=dp[i][M];    printf("%d\n",ans);}


2 0
原创粉丝点击