bzoj 4349 最小树形图——朱刘算法
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4349.
学习博客:http://www.cnblogs.com/xzxl/p/7243466.html
关于这道题,图的边权不是 代价*次数 , 而就是一次的代价,因为只要考虑每个堡垒第一次以什么代价被打,之后都可以用最低代价打它。
注意 “不需要攻打” 的堡垒。注意重连边的时候把边的端点改成新的标号。
#include<cstdio> #include<cstring> #include<algorithm> #define db double using namespace std; const int N=55,M=2505; const db INF=1e6; int n,b[N];db a[N],mn[N]; bool ok[N]; namespace DMST{ int xnt,pre[N],id[N],vis[N]; db in[N]; struct Ed{ int x,y; db w; Ed(int x=0,int y=0,db w=0):x(x),y(y),w(w) {} }ed[M]; void add(int x,int y,db z){ ed[++xnt]=Ed(x,y,z); } db solve(int V,int E) { db ret=0; while(1) { for(int i=1;i<=V;i++)in[i]=INF,id[i]=vis[i]=0; for(int i=1,u,v;i<=E;i++) { u=ed[i].x; v=ed[i].y; if(in[v]>ed[i].w)in[v]=ed[i].w, pre[v]=u; } for(int i=1;i<=V;i++) { if(in[i]==INF)return -1; ret+=in[i];} int cnt=0; for(int i=1;i<=V;i++) { vis[i]=i; int cr=pre[i]; while(cr&&!vis[cr]) vis[cr]=i, cr=pre[cr]; if(vis[cr]==i) { id[cr]=++cnt; cr=pre[cr]; while(!id[cr])id[cr]=cnt, cr=pre[cr]; } } if(!cnt)return ret; for(int i=1;i<=V;i++)if(!id[i])id[i]=++cnt; V=cnt; int tE=E; E=0; for(int i=1,u,v;i<=tE;i++) { u=ed[i].x; v=ed[i].y; if(id[u]==id[v])continue; ed[++E].x=id[u]; ed[E].y=id[v];//id[] ed[E].w=ed[i].w-in[v]; } } } } int main() { int tn;scanf("%d",&tn); for(int i=1;i<=tn;i++) { scanf("%lf%d",&a[i],&b[i]); if(!b[i]){ ok[i]=1;continue;} DMST::id[i]=++n; DMST::add(0,DMST::id[i],a[i]); } int m; db d; scanf("%d",&m); for(int i=1,u,v;i<=m;i++) { scanf("%d%d%lf",&u,&v,&d); if(ok[u]||ok[v])continue; DMST::add(DMST::id[u],DMST::id[v],d); a[v]=min(a[v],d); } db ans=DMST::solve(n,DMST::xnt); for(int i=1;i<=tn;i++)//tn if(!ok[i])ans+=(b[i]-1)*a[i]; printf("%.2f\n",ans); return 0; }