【Luogu】P2469星际竞速(费用流)
费用流,类似最小路径覆盖。
从起点向i连一条容量1费用0的边,从i'向终点连一条容量1费用0的边;
从起点向i'连一条容量1费用为瞬移的边,从i向j'连一条容量1费用为边权的边。
然后跑就可以了。
#include<cstdio> #include<cstdlib> #include<cctype> #include<cstring> #include<algorithm> #include<queue> #define maxn 200020 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } struct Edge{ int next,from,to,val,dis; }edge[maxn]; int head[maxn],num; inline void addedge(int from,int to,int val,int dis){ edge[++num]=(Edge){head[from],from,to,val,dis}; head[from]=num; } inline void add(int from,int to,int val,int dis){ addedge(from,to,val,dis); addedge(to,from,0,-dis); } inline int count(int i){ return i&1?i+1:i-1; } int dis[maxn]; int pre[maxn]; bool vis[maxn]; int Start,End; int spfa(){ memset(dis,127/3,sizeof(dis)); dis[Start]=0; memset(pre,0,sizeof(pre)); queue<int>q; q.push(Start); while(!q.empty()){ int from=q.front(); q.pop(); vis[from]=0; //printf("%d\n",from); for(int i=head[from];i;i=edge[i].next){ int to=edge[i].to; if(edge[i].val<=0||dis[to]<=dis[from]+edge[i].dis) continue; dis[to]=dis[from]+edge[i].dis; pre[to]=i; if(vis[to]) continue; vis[to]=1; q.push(to); } } if(pre[End]==0) return 0; int now=End; while(now!=Start){ int ret=pre[now]; edge[ret].val--; edge[count(ret)].val++; now=edge[ret].from; } return dis[End]; } int main(){ int n=read(),m=read(); End=n*2+1; for(int i=1;i<=n;++i){ add(Start,i+n,1,read()); add(Start,i,1,0); add(i+n,End,1,0); } for(int i=1;i<=m;++i){ int from=read(),to=read(),val=read(); if(from>to) swap(from,to); add(from,to+n,1,val); } int ans=0; while(1){ int now=spfa(); if(now==0) break; ans+=now; } printf("%d",ans); return 0; }