P2469 [SDOI2010]星际竞速

(话说如果用上下界网络流就是智障题目了emm)

源 点 到 i 费 用 0 , 流 量 1 源点到i费用0,流量1 i0,1

源 点 连 向 i ′ , 费 用 为 瞬 移 的 费 用 , 流 量 1 源点连向i',费用为瞬移的费用,流量1 i,,1

i ′ 到 汇 点 费 用 0 , 流 量 1 i'到汇点费用0,流量1 i0,1

若 有 u 到 v 的 边 , 则 u ′ 连 向 v , 费 用 为 花 费 , 流 量 1 若有u到v的边,则u'连向v,费用为花费,流量1 uv,uv,,1

比 如 现 在 有 a − > b 和 b − > c 的 边 , 图 大 概 这 样 子 比如现在有a->b和b->c的边,图大概这样子 a>bb>c,

P2469 [SDOI2010]星际竞速(费用流)_#include

考虑这样做的正确性

把 点 i 拆 点 , 其 实 i 相 当 于 入 点 , i ′ 相 当 于 出 点 把点i拆点,其实i相当于入点,i'相当于出点 i,i,i

我 们 最 后 走 的 路 径 一 定 是 一 条 一 条 的 链 , 链 首 是 瞬 移 过 去 的 我们最后走的路径一定是一条一条的链,链首是瞬移过去的 ,

那 假 如 说 明 链 首 出 点 没 被 任 意 一 条 路 径 经 过 那假如说明链首出点没被任意一条路径经过

因 为 是 最 大 流 , 所 以 汇 点 会 走 向 链 首 的 出 点 , 相 当 于 花 费 了 链 首 的 瞬 移 代 价 因为是最大流,所以汇点会走向链首的出点,相当于花费了链首的瞬移代价 ,,

如 果 某 个 点 不 属 于 链 首 也 不 属 于 链 尾 , 说 明 入 点 和 出 点 有 连 边 如果某个点不属于链首也不属于链尾,说明入点和出点有连边 ,

如 图 所 示 , 相 当 于 两 次 匹 配 , 只 花 费 匹 配 的 高 速 航 行 的 费 用 如图所示,相当于两次匹配,只花费匹配的高速航行的费用 ,,

所以正确

(代码很简单)

#include <bits/stdc++.h>
using namespace std;
const int N=2e3+10;
const int M=2e6+10;
const int inf=1e8;
int d(){int x; scanf("%d",&x); return x;}
int n,m,p,s,t,a[N],fans,cans; 
struct edge{
	int adj,nex,fw,r;
}e[M];
int g[N],top=1;
void add(int x,int y,int z,int w){
	e[++top]=(edge){y,g[x],z,w};
	g[x]=top;
}
void Add(int x,int y,int z,int w){
	// printf("%d-%d %d %d\n",x,y,z,w);
	add(x,y,z,w),add(y,x,0,-w);
}
int dep[N],cur[N];
bool vis[N];
queue<int> Q;
bool spfa(){ //模板就不用说了
	for(int i=1;i<=p;i++)
		vis[i]=0,dep[i]=inf,cur[i]=g[i];
	Q.push(s),vis[s]=1,dep[s]=0;
	while(Q.size()){
		int x=Q.front(); Q.pop();
		vis[x]=0;
		for(int i=g[x];i;i=e[i].nex){
			int to=e[i].adj,d=e[i].r;
			if(e[i].fw&&dep[to]>dep[x]+d){
				dep[to]=dep[x]+d;
				if(!vis[to]){
					vis[to]=1;
					Q.push(to);
				}
			}
		}
	}
	return dep[t]!=inf;
}
int dfs(int x,int F){
	if(!F||x==t)
		return F;
	int flow=0,f;
	vis[x]=1;
	for(int i=cur[x];i;i=e[i].nex){
		int to=e[i].adj; cur[x]=i;
		if(!vis[to]&&dep[x]+e[i].r==dep[to]&&
		(f=dfs(to,min(F,e[i].fw)))>0){
			e[i].fw-=f;
			e[i^1].fw+=f;
			flow+=f,F-=f;
			if(!F){
				vis[x]=0;
				break;
			} 
		}
	}
	return flow;
}
int main(){
	n=d(),m=d(),p=t=2*n+2,s=t-1;
	for(int i=1,x;i<=n;i++){ //如上说明连边
		a[i]=d(); Add(i+n,t,1,0); 
		Add(s,i,1,0),Add(s,i+n,1,a[i]);
	}
	for(int i=1;i<=m;i++){
		int x=d(),y=d(),z=d();
		if(x>y) swap(x,y);
		if(z<a[y]) Add(x,y+n,1,z);
	}
	while(spfa()){ //跑最小费用最大流。
		int D=dfs(s,inf);
		fans+=D,cans+=D*dep[t];
	}
	printf("%d\n",cans);
	return 0;
}