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个整数,表示这个有向图的树形图个数
样例输入
4
0100
0010
0001
1000
样例输出
4
提示 对于100%的数据,n≤ 8.
题意就是给你若干条有向边,求有多少种不同的生成树。
因为题目数据规模太水,所以可以直接暴力,即枚举树上每一个点的父亲,判环可以用并查集暴力。
但因为根节点没有父亲,所以需要提前枚举。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
#define MAXN 8
#define MAXM
#define INF 0x3f3f3f3f
typedef 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 0x3f3f3f3f
typedef 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);
}