(话说如果用上下界网络流就是智障题目了emm)
源 点 到 i 费 用 0 , 流 量 1 源点到i费用0,流量1 源点到i费用0,流量1
源 点 连 向 i ′ , 费 用 为 瞬 移 的 费 用 , 流 量 1 源点连向i',费用为瞬移的费用,流量1 源点连向i′,费用为瞬移的费用,流量1
i ′ 到 汇 点 费 用 0 , 流 量 1 i'到汇点费用0,流量1 i′到汇点费用0,流量1
若 有 u 到 v 的 边 , 则 u ′ 连 向 v , 费 用 为 花 费 , 流 量 1 若有u到v的边,则u'连向v,费用为花费,流量1 若有u到v的边,则u′连向v,费用为花费,流量1
比 如 现 在 有 a − > b 和 b − > c 的 边 , 图 大 概 这 样 子 比如现在有a->b和b->c的边,图大概这样子 比如现在有a−>b和b−>c的边,图大概这样子
考虑这样做的正确性
把 点 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;
}