最小树形图 jsoi shopping

 

 

题目大意:最小树形图;

最小树形图:给你一个带权有向图,选出一些边构成一颗有根树,并使得这棵树的边权之和最小;

 

下面为基本流程.

最小树形图模型首先给定点集和一个根,若干条带权有向边,求从根出发的一个子图,边数为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;
}


 

你可能感兴趣的:(最小树形图 jsoi shopping)