题目大意:最小树形图;
最小树形图:给你一个带权有向图,选出一些边构成一颗有根树,并使得这棵树的边权之和最小;
下面为基本流程.
最小树形图模型首先给定点集和一个根,若干条带权有向边,求从根出发的一个子图,边数为N-1,能从根到所有节点,并且边权和最小.
首先可以肯定的是,如果这个图从根走一遍后发现不连通,那么肯定无最小树形图.
否则每个仍在图中的点i记对于i点的最小权入边对应的入点为pre[i]
如果没有环,直接将所有cost[pre[i],i]相加记为ans
否则,缩环为点,记环为v,环上的任意点位vv,不在环上的任意点为u,将环上的边权和加入ans,改cost[v,u]=min{cost[vv,u]},cost[u,v]=min(cost[u,vv]-cost[pre[vv],vv])
再继续修改pre[i],并判断还有没有环.
这样改造后,对于任意一个环,最后加上某条入这条环的边,因为边权的改造,必将消去环上一条边,使得最小树形图中边位N-1且连通所有点.
by ld from ly
这个算法的复杂度最坏是O(NM)的。
ld发明了个带LAZY标号的可并堆。具体实现是用的斜堆,这样这道题的复杂度就可以通过数据结构优化,但我没看懂,大家可以去jasonzhu8的博客看。
我用的是递归,实现起来有点麻烦,有些操作是冗余的。。。
下面是代码。ans1是求最小树形图,ans2可以忽视。话说因为我没设cost1导致调了一上午。。。
const maxn=100; max=1073741819; inf='shopping.in'; ouf='shopping.out'; anf='shopping1.out'; var n,m,s2,s:longint; ans1,ans2:real; v,cle:array[0..maxn]of boolean; b,c,b1,c1:array[0..maxn,0..maxn]of longint; cost,cost1:array[0..maxn,0..maxn]of real; st,pre,num,visit,pre1:array[0..maxn]of longint; min:array[0..maxn]of real; function dfs(x:longint;var z:boolean):longint; var i,j,ne,na:longint; cos:real; begin if x=s then begin dfs:=0;exit end; dfs:=0;visit[x]:=1; if visit[pre[x]]=2 then begin visit[x]:=2;exit end; if (visit[pre[x]]=1) then begin dfs:=pre[x];z:=true;cle[x]:=false;v[x]:=false;ans1:=ans1+cost[pre[x],x]; inc(s2);st[s2]:=x;visit[x]:=2;exit end; if visit[pre[x]]=0 then dfs:=dfs(pre[x],z); visit[x]:=2; if dfs=-1 then exit; if dfs=0 then begin{ ans1:=ans1+cost[pre[x],x];cle[x]:=false; }exit end; v[x]:=false; if x<>dfs then begin inc(s2);st[s2]:=x;cle[x]:=false;ans1:=ans1+cost[pre[x],x];exit end; i:=1;ans1:=ans1+cost[pre[x],x];inc(s2);st[s2]:=x; c1:=c;b1:=b;pre1:=pre;cost1:=cost; while i<=s2 do begin ne:=st[i]; for j:=1 to b[ne,0] do if (v[b[ne,j]])and(cle[b[ne,j]]) then begin na:=b[ne,j]; if cost1[x,na]>max then begin inc(b1[x,0]);b1[x,b1[x,0]]:=na;inc(c1[na,0]);c1[na,c1[na,0]]:=x end; if cost[ne,na]<cost1[x,na] then cost1[x,na]:=cost[ne,na]; if (cost[ne,na]<cost1[pre1[na],na])or(not cle[pre1[na]]) then pre1[na]:=x end; for j:=1 to c[ne,0] do if (v[c[ne,j]])and(cle[c[ne,j]]) then begin na:=c[ne,j]; if cost[na,x]>max then begin inc(b1[na,0]);b1[na,b1[na,0]]:=x;inc(c1[x,0]);c1[x,c1[x,0]]:=na end; cos:=cost[na,ne]-cost[pre[ne],ne]; if cos<cost1[na,x] then cost1[na,x]:=cos; if (cos<cost1[pre1[x],x])or(not cle[pre1[x]]) then pre1[x]:=na end; inc(i) end; b:=b1;c:=c1;pre:=pre1;cost:=cost1;dfs:=-1 end; procedure dfs2(x:longint); begin if x=0 then exit; visit[x]:=1; if visit[pre[x]]=0 then dfs2(pre[x]); visit[x]:=2;cle[x]:=false; ans1:=ans1+cost[pre[x],x]; end; procedure getans; var i:longint; begin fillchar(visit,sizeof(visit),0); for i:=1 to n do if (cle[i])and(visit[i]=0) then dfs2(i) end; procedure mintree; var z:boolean; i:longint; begin z:=false; fillchar(visit,sizeof(visit),0); fillchar(v,sizeof(v),true); fillchar(st,sizeof(st),0);s2:=0; for i:=1 to n do if (cle[i])and(visit[i]=0) then if dfs(i,z)<>0 then break; if z then mintree else getans end; procedure origin; var i:longint; begin for i:=1 to n do begin pre[i]:=n+1;cost[pre[i],i]:=max end; fillchar(cle,sizeof(cle),true); fillchar(cost,sizeof(cost),127); ans1:=0;ans2:=0; fillchar(min,sizeof(min),127); fillchar(c,sizeof(c),0);fillchar(b,sizeof(b),0); end; procedure init; var i,x,y:longint; xx,z:real; begin assign(input,inf);reset(input); readln(n); origin; s:=0; for i:=1 to n do begin readln(xx,y); num[i]:=y; if y>0 then begin inc(b[s,0]);b[s,b[s,0]]:=i;cost[s,i]:=xx; inc(c[i,0]);c[i,c[i,0]]:=s; if cost[s,i]<cost[pre[i],i] then pre[i]:=s; if xx<min[i] then min[i]:=xx end else cle[i]:=false end; readln(m); for i:=1 to m do begin readln(x,y,z); if (num[x]>0)and(num[y]>0) then begin inc(b[x,0]);b[x,b[x,0]]:=y;cost[x,y]:=z; inc(c[y,0]);c[y,c[y,0]]:=x; if cost[x,y]<cost[pre[y],y] then pre[y]:=x; if z<min[y] then min[y]:=z end end; for i:=1 to n do if num[i]>0 then ans2:=ans2+(num[i]-1)*min[i]; mintree; assign(output,ouf);rewrite(output); writeln(ans1+ans2:0:2); close(output); close(input) end; begin init end.
c++版
#include <cstdio> #include <cstdlib> #include <cstring> const int oo=1073741819; double c[100][100],f[100][100],ans1,ans2,tmp[1000]; int pre[1000],b[1000],st[1000],ts[1000],v[1000],vis[1000],num[1000]; int l,r,n,m,time,flag; int find(int x) {if (b[x]!=x) b[x]=find(b[x]);return b[x];} double min(double x,double y) {return (x<y) ? x : y;} void mysoul() { int i,j; double cnt; l=0; for (;st[r]!=st[0];r--) ts[++l]=st[r]; ts[++l]=st[r],r--; for (i=1;i<=l;i++) { for (j=0;j<=n;j++) f[ts[i]][j]=c[ts[i]][j],f[j][ts[i]]=c[j][ts[i]]; b[ts[i]]=st[0]; ans1+=c[pre[ts[i]]][ts[i]]; } for (i=0;i<=n;i++) c[i][st[0]]=f[i][st[0]]-f[pre[st[0]]][st[0]]; for (i=0;i<=n;i++) for (j=1;j<=l-1;j++) { c[i][st[0]]=min(c[i][st[0]],f[i][ts[j]]-f[pre[ts[j]]][ts[j]]); c[st[0]][i]=min(c[st[0]][i],f[ts[j]][i]); } cnt=oo; for (i=0;i<=n;i++) { if (b[i]!=i || i==st[0]) continue; if (pre[i]<oo) { pre[i]=find(b[pre[i]]); if (c[st[0]][i]<c[pre[i]][i]) pre[i]=st[0]; } else if (c[st[0]][i]<oo) pre[i]=st[0]; if (c[i][st[0]]<cnt) cnt=c[i][st[0]],pre[st[0]]=i; } if (cnt>=oo) pre[st[0]]=oo; st[0]=oo; } void dfs(int x) { v[x]=time,vis[x]=1; st[++r]=x; if (pre[x]<oo) { if (v[pre[x]]!=time) dfs(pre[x]); if (vis[pre[x]]==1) { if (st[0]==oo) st[0]=pre[x]; else if (st[0]==x) { mysoul(); flag=time; } return ; } } r--,vis[x]=2; } void mydream() { int i; for (i=1;i<=n;i++) b[i]=i; ans1=0; for (flag=0;flag==time;) { time++; for (i=1;i<=n;i++) { if (v[i]==time || b[i]!=i) continue; st[r=0]=oo,dfs(i); } } for (i=0;i<=n;i++) { if (pre[i]>=oo || b[i]!=i) continue; ans1+=c[pre[i]][i]; } } void init() { int i,x,y,j; double z,cnt; scanf("%d\n",&n); memset(c,127,sizeof(c));memset(pre,127,sizeof(pre)); for (i=1;i<=n;i++) { scanf("%lf%d\n",&tmp[i],&num[i]); c[0][i]=tmp[i]; } scanf("%d\n",&m); for (i=1;i<=m;i++) { scanf("%d%d%lf\n",&x,&y,&z); c[x][y]=z,tmp[y]=min(tmp[y],z); } for (i=0;i<=n;i++) { cnt=oo; for (j=0;j<=n;j++) { if (c[j][i]>=oo) continue; if (c[j][i]<cnt) cnt=c[j][i],pre[i]=j; } } mydream(); for (i=1;i<=n;i++) if (num[i]) ans2+=(num[i]-1)*tmp[i]; printf("%.2lf\n",ans1+ans2); } int main() { freopen("sxt1988.in","r",stdin); freopen("sxt1988.out","w",stdout); init(); return 0; }