最小树形图
有根的情况: 直接求.
无根的情况: 建立超级根节点,向每个点连有向边,边权大于所有边权和即可.
<span style="font-family:FangSong_GB2312;font-size:14px;"><strong>const int N=1010;
const int inf=1000000000;
struct node{
int oldu,oldv; //原始点
int u,v,w; //缩图后的点
}e[N*N];
int n,m,rt;
int pre[N],ID[N],vis[N];
int in[N]; //最小入边权值
int Directed_MST(int root,int n,int m){
int oldroot=root;
long long ret=0;
while (1){
for (int i=1;i<=n;i++) in[i]=inf;
//找到最小入边
for (int i=1;i<=m;i++){
int u=e[i].u;
int v=e[i].v;
if (u!=v&&e[i].w<in[v]){
pre[v]=u;in[v]=e[i].w;
if (e[i].oldu==oldroot) rt=e[i].oldv;
}
}
for (int i=1;i<=n;i++){
if (i==root) continue;
if (in[i]==inf) return -1;
}
//找环
int nn=0;//新图的节点个数
memset(ID,-1,sizeof(ID));
memset(vis,-1,sizeof(vis));
in[root]=0;
for (int i=1;i<=n;i++){ //标记每一个环
ret+=in[i];
int v=i;
while (v!=root&&ID[v]==-1&&vis[v]!=i){
vis[v]=i;v=pre[v];
}
if (v!=root&&ID[v]==-1){ //将环缩成点
ID[v]=++nn;
for (int u=pre[v];u!=v;u=pre[u]) ID[u]=nn;
}
}
if (nn==0) break; //无环
for (int i=1;i<=n;i++) //每一个不在环上的点添加到图中
if (ID[i]==-1) ID[i]=++nn;
//缩点重新标记
for (int i=1;i<=m;i++){
int u=e[i].u;
int v=e[i].v;
e[i].u=ID[u];
e[i].v=ID[v];
if (u!=v) e[i].w-=in[v];
}
n=nn;
root=ID[root];
}
return ret;
}
//输入
for (int i=1;i<=m;i++){
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
e[i].oldu=e[i].u;e[i].oldv=e[i].v;
sum+=e[i].w;
}
//建立超级根节点n+1
for (int i=1;i<=n;i++){
e[++m].u=n+1;e[m].v=i;e[m].w=sum+1;
e[m].oldu=e[m].u;e[m].oldv=e[m].v;
}
n++;
</strong></span>
如果ret!=-1&&ret<2*sum+2 表示有最小树形图结果为ret-sum-1,反正没有。
题目:
//poj 3164(模板题)
//uva 11183(模板题)
//hdu 2121(需要建立一个超级根节点)
//hdu 3072(最小树形图+强连通)
//hdu 4009(需要建立一个超级根节点)