题意:给出一个有向图的传递背包,求原图最少有多少边。
思路:用强连通缩点(在一个强连通里面,剩下一个简单环就可以了,n=1特殊处理),再用floyd简化缩点后的图(就是a->b,b->c,a->c,就可以吧a->c去掉),最后统计。
#include<stdio.h>
#include<string.h>
#include<queue>
#define min(a,b) (a<b?a:b)
#define maxn 300
#define maxe 50000
using namespace std;
int dfn[maxn],low[maxn],m[maxn][maxn],mi[maxn][maxn];
int head[maxn],stack[maxn],belong[maxn];
int come[maxn],cou[maxn];
int times,top,scc,EN,ans,ENN;
struct ED
{
int v,next;
}edge[maxe],ed[maxe];
void addedge(int u,int v)
{
edge[EN].v=v;
edge[EN].next=head[u];
head[u]=EN++;
}
void tarjan(int u)
{
dfn[u]=low[u]=++times;
stack[++top]=u;
int v;
for(int i=head[u];~i;i=edge[i].next)
{
v=edge[i].v;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(!belong[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
scc++;
do
{
v=stack[top--];
belong[v]=scc;
cou[scc]++;
}while(v!=u);
if(cou[scc]==1)
cou[scc]=0;
}
}
void floy(int N,int mat[][maxn],int mi[][maxn])
{
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
mi[i][j]=mat[i][j];
for(int i=1;i<=N;i++)
mi[i][i]=0;
for(int k=1;k<=N;k++)
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
if(mi[i][k]&&mi[k][j])
mi[i][j]=0;
return;
}
int main()
{
int N,c;
while(scanf("%d",&N)!=EOF)
{
top=EN=times=scc=ans=0;
memset(head,-1,sizeof(head));
memset(belong,0,sizeof(belong));
memset(dfn,0,sizeof(dfn));
memset(cou,0,sizeof(cou));
memset(m,0,sizeof(m));
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
{
scanf("%d",&c);
if(c&&i!=j)
addedge(i,j);
}
for(int i=0;i<N;i++)
if(!dfn[i])
tarjan(i);
for(int u=0;u<N;u++)
for(int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].v;
if(belong[u]!=belong[v])
m[belong[u]][belong[v]]=1;
}
for(int i=1;i<=scc;i++)
ans+=cou[i];
floy(scc,m,mi);
for(int i=1;i<=scc;i++)
for(int j=1;j<=scc;j++)
if(mi[i][j])
ans++;
printf("%d\n",ans);
}
return 0;
}