题意
给一个n个点m条边的有向图,边有边权问以任意一个点为根的最小树形图的权值是多少,并且要求出编号最小的根。
n≤1000,m≤10000
n
≤
1000
,
m
≤
10000
分析
做了一天终于做出来了。
求最小树形图要用到朱刘算法,过程大概是每次对所有点找一条权值最小的入边,若存在点没有入边则最小树形图不存在;否则的话,若不存在环,则表明找到了最小树形图,不然就把环缩点,并把一些边的边权更新一下,并继续上述步骤。
这题因为是根任意,则可以定一个虚点,然后从虚电往每个点都连一条权值为inf的边,然后做最小树形图。若最终答案超过2inf,则表示不存在最小树形图。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
typedef long long LL;
const int N=1005;
const LL inf=(LL)1e18;
int n,m,cnt,last[N],tim[N],pre[N],root,id[N];
bool flag[N];
LL ans,mn[N];
struct edge{int u,v;LL w;}e[N*20];
void addedge(int u,int v,LL w)
{
e[++cnt].u=u;e[cnt].v=v;e[cnt].w=w;
}
bool solve(int rt)
{
for (int i=1;i<=n;i++) id[i]=i;
while (1)
{
for (int i=1;i<=n;i++) mn[i]=inf;
for (int i=1;i<=cnt;i++)
{
int u=e[i].u,v=e[i].v;
if (u!=v&&e[i].w<mn[v])
{
mn[v]=e[i].w;pre[v]=u;
if (u==rt) root=i;
}
}
for (int i=1;i<=n;i++) if (i!=rt&&mn[i]==inf) return 0;
memset(id,0,sizeof(id));
memset(tim,0,sizeof(tim));
memset(flag,0,sizeof(flag));
int tot=0;
for (int i=1;i<=n;i++) if (!tim[i])
{
int x=i;
while (!id[x]&&tim[x]!=i&&x!=rt) tim[x]=i,x=pre[x];
if (tim[x]==i)
{
tot++;
for (int y=pre[x];y!=x;y=pre[y]) id[y]=tot,ans+=mn[y],flag[y]=1;
id[x]=tot;ans+=mn[x];flag[x]=1;
}
}
if (!tot)
{
for (int i=1;i<=n;i++) if (i!=rt) ans+=mn[i];
return ans<inf*2/10;
}
for (int i=1;i<=n;i++) if (!id[i]) id[i]=++tot;
for (int i=1;i<=cnt;i++)
{
if (flag[e[i].v]) e[i].w-=mn[e[i].v];
e[i].u=id[e[i].u];e[i].v=id[e[i].v];
}
n=tot;rt=id[rt];
}
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
ans=cnt=0;
for (int i=1;i<=m;i++)
{
int x,y;LL z;scanf("%d%d%lld",&x,&y,&z);
addedge(x+1,y+1,z);
}
for (int i=1;i<=n;i++) addedge(n+1,i,inf/10);
n++;
if (!solve(n)) puts("impossible");
else printf("%lld %d\n",ans-inf/10,root-m-1);
puts("");
}
return 0;
}