题目大意:最小树形图;
最小树形图:给你一个带权有向图,选出一些边构成一颗有根树,并使得这棵树的边权之和最小;
下面为基本流程.
最小树形图模型首先给定点集和一个根,若干条带权有向边,求从根出发的一个子图,边数为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导致调了一上午。。。
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;
}