[SDOI2010] 星际竞速
Description:
给定一个图,每次只能从编号小的节点移动到另一个,代价是边权\(w_i\),或者直接花费\(a_i\)的代价瞬移到\(i\)点,求遍历所有节点的最小代价
Hint:
\(n \le 800\)
Solution:
要求必须经过每一个点,我们就把\(S\)连向\(a'\)容1费\(a_i\),再把\(a'\)连一条容1费0到T
同时我们把每条边\(a->b\)连\(a->b'\) 容1费\(w_i\),再把\(S\)连向\(a\)容1费0,跑最小费用最大流就行了
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1
#define rs p<<1|1
using namespace std;
typedef long long ll;
const int mxn=1e5+5,inf=1e9;
int n,m,k,cnt,hd[mxn];
int S,T,ans,cost,dis[mxn],pre[mxn],bl[mxn],f[mxn],vis[mxn];
inline int read() {
char c=getchar(); int x=0,f=1;
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
return x*f;
}
inline void chkmax(int &x,int y) {if(x<y) x=y;}
inline void chkmin(int &x,int y) {if(x>y) x=y;}
struct ed {
int to,nxt,w,val;
}t[mxn<<1];
inline void add(int u,int v,int w,int z) {
t[cnt]=(ed) {v,hd[u],w,z}; hd[u]=cnt++;
t[cnt]=(ed) {u,hd[v],0,-z}; hd[v]=cnt++;
}
int spfa() {
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
memset(dis,0x3f,sizeof(dis));
memset(f,0x3f,sizeof(f));
queue<int > q; q.push(S);
dis[S]=pre[S]=0;
while(!q.empty()) {
int u=q.front(); q.pop();
vis[u]=0;
for(int i=hd[u];i!=-1;i=t[i].nxt) {
int v=t[i].to;
if(t[i].w>0&&dis[v]>dis[u]+t[i].val) {
dis[v]=dis[u]+t[i].val; pre[v]=u;
bl[v]=i; f[v]=min(f[u],t[i].w);
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
return pre[T];
}
void Dinic() {
while(spfa()) {
ans+=f[T];
cost+=f[T]*dis[T];
for(int i=T;i!=S;i=pre[i]) {
t[bl[i]].w-=f[T];
t[bl[i]^1].w+=f[T];
}
}
}
int get(int x,int y) {
return (x-1)*n+y;
}
int main()
{
n=read(); m=read(); T=n*2+1; int u,v,w,x; memset(hd,-1,sizeof(hd));
for(int i=1;i<=n;++i) x=read(),add(S,i+n,1,x);
for(int i=1;i<=n;++i) add(i+n,T,1,0),add(S,i,1,0);
for(int i=1;i<=m;++i) {
u=read(); v=read(); w=read();
if(u>v) swap(u,v);
add(u,v+n,1,w);
}
Dinic(); printf("%d",cost);
return 0;
}