[BZOJ4349]最小树形图

显然先选每个点都取一遍然后再取满次数最优,用最小树形图决定第一次取的顺序。

朱刘算法的流程是(总复杂度O(nm)):

1.对除根外所有点,找到所有指向它的边中权值最小的那一条,记其权值为ind[]。

2.找到所有不包含根的、由(1)中找到的那些边构成的环,并将环缩点。若没有这样的环则结束。

3.将所有缩点后不是自环的边的权值,减去边的终点的ind。

主要思路就是贪心+调整,具体看代码实现。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 using namespace std;
 5 
 6 const int N=61,M=21000;
 7 const double inf=1e10;
 8 int n=1,m,m1,n1,x,y,cnt,num[N],pos[N],pre[N],id[N],vis[N];
 9 double a[N],ans,v,ind[N];
10 struct E{ int u,v; double w; }e[M];
11 
12 double Zhuliu(int rt,int n,int m){
13     int tn,tm; double res=0;
14     while (1){
15         rep(i,1,n) ind[i]=inf,pre[i]=id[i]=vis[i]=0;
16         tn=tm=ind[rt]=0;
17         rep(i,1,m) if (e[i].w<ind[e[i].v]) ind[e[i].v]=e[i].w,pre[e[i].v]=e[i].u;
18         rep(i,1,n){
19             int x=i; res+=ind[i];
20             while (x!=rt && vis[x]!=i && !id[x]) vis[x]=i,x=pre[x];
21             if (x!=rt && !id[x]){
22                 id[x]=++tn;
23                 for (int k=pre[x]; k!=x; k=pre[k]) id[k]=tn;
24             }
25         }
26         if (!tn) break;
27         rep(i,1,n) if (!id[i]) id[i]=++tn;
28         rep(i,1,m) if (id[e[i].u]!=id[e[i].v]) e[++tm]=(E){id[e[i].u],id[e[i].v],e[i].w-ind[e[i].v]};
29         n=tn; m=tm; rt=id[rt];
30     }
31     return res;
32 }
33 
34 int main(){
35     freopen("bzoj4349.in","r",stdin);
36     freopen("bzoj4349.out","w",stdout);
37     scanf("%d",&n1);
38     rep(i,1,n1){
39         scanf("%lf%d",&v,&x);
40         if (x) pos[i]=++n,e[++m]=(E){1,n,v},a[n]=v,num[n]=x-1;
41     }
42     scanf("%d",&m1);
43     rep(i,1,m1){
44         scanf("%d%d%lf",&x,&y,&v);
45         if (!pos[x] || !pos[y]) continue;
46         e[++m]=(E){pos[x],pos[y],v}; a[pos[y]]=min(a[pos[y]],v);
47     }
48     rep(i,2,n) ans+=a[i]*num[i];
49     printf("%.2lf\n",Zhuliu(1,n,m)+ans);
50     return 0;
51 }

 

posted @ 2019-03-26 19:34  HocRiser  阅读(196)  评论(0编辑  收藏  举报