中文题面,还特长。请各位自行品尝→星际竞速
分析:
首先拆穿题面一个伪装。题面里竟然明摆着说是双向图,后来又变成了“只能由每个星球飞往引力比它大的星球”,所以这就是出题人凑字数的把戏,我们直接将其看作单向边就好了(不过如果给的顺序反了要swap一下)
这个题目的图比较好建,比起那些奇奇怪怪的转化,我们只需要按着题面建图就好了。
我们要满足“每个点恰好访问一次”这个限制,在求最小费用的题目里,我们应该用最大流来保证这种限制(就是说,当全图为最大流时,即为访问每个点恰好一次)
我们按照在这个思路去建图。将每个星球拆成一个入点和一个出点
从原点S向每个星球的入点连一条容量为1费用为0的边。
从原点向每个点的出点连一条容量为1费用为瞬移定位时间的边(表示不走寻常路的流?)
假如在原图中存在(x->y)的一条边,那么我们从x的入点向y的出点连容量为1费用为航路所需时间的边(表示从x点走向了y点完成了这条航道的使命?)
最小费用最大流,输出费用,结束。
代码:
1 #include<bits/stdc++.h> 2 #define ms(a,x) memset(a,x,sizeof(a)) 3 using namespace std; 4 const int N=5000,M=100000; 5 const int inf=0x3f3f3f3f; 6 queue<int>q;int c1[N],c2[N],cnt; 7 struct node{int y,z,f,nxt;}e[M]; 8 int lst[N],pre[N],S,T;bool vis[N]; 9 int h[N],c=1,n,m,d[N],f[N],ans,tot; 10 void add(int x,int y,int l,int z){ 11 e[++c]=(node){y,z,l,h[x]};h[x]=c; 12 e[++c]=(node){x,-z,0,h[y]};h[y]=c; 13 } bool spfa(){ 14 for(int i=S;i<=T;i++) pre[i]=-1, 15 f[i]=inf,lst[i]=vis[i]=0,d[i]=inf; 16 q.push(S);d[S]=0;pre[S]=0; 17 while(q.size()){ 18 int x=q.front();q.pop();vis[x]=0; 19 for(int i=h[x],y;~i;i=e[i].nxt) 20 if(d[y=e[i].y]>d[x]+e[i].z&&e[i].f){ 21 d[y]=d[x]+e[i].z;pre[y]=x;lst[y]=i; 22 f[y]=min(f[x],e[i].f); 23 if(!vis[y]) vis[y]=1,q.push(y); 24 } 25 } return (pre[T]!=-1); 26 } void solve(){ 27 while(spfa()){ 28 ans+=d[T]*f[T];int x=T; 29 while(x) e[lst[x]].f-=f[T], 30 e[lst[x]^1].f+=f[T],x=pre[x]; 31 } return ; 32 } int main(){ ms(h,-1); 33 scanf("%d%d",&n,&m);S=0;cnt=0; 34 for(int i=1;i<=n;i++) 35 c1[i]=++cnt,c2[i]=++cnt;T=++cnt; 36 for(int i=1,x;i<=n;i++) 37 scanf("%d",&x),add(S,c2[i],1,x); 38 for(int i=1;i<=n;i++) add(S,c1[i],1,0); 39 for(int i=1;i<=n;i++) add(c2[i],T,1,0); 40 for(int i=1,x,y,z;i<=m;i++){ 41 scanf("%d%d%d",&x,&y,&z);if(x>y) 42 swap(x,y);add(c1[x],c2[y],1,z); 43 } solve(); 44 printf("%d ",ans);return 0; 45 }