题解
类似DAG上的最小路径覆盖
最小费用流,建图 令源点为
S
,汇点为
S向所有入点连容量为1,费用为0的边
S向所有出点连容量为1,费用为 ai 的边
所有出点与T连容量为1,费用为0的边
对于有边的 (ui,vi) 其中 ui<vi ,从 ui 的入点连向 vi 的出点容量为1,费用为 wi 的边
理解一下:相当于如果要到
T
就要走到出点 就要从入点来或者
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define N 2*805
#define M 15005
using namespace std;
struct edge{int to,nxt,v,c;}e[M<<3];
int n,m,cnt,s,t,head[N],dis[N],from[N],pre[N];
bool v[N];
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
int a=0;char f=1,c=nc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=nc();}
while(c>='0'&&c<='9'){a=a*10+c-'0';c=nc();}
return a*f;
}
inline void add(int x,int y,int v,int c){
e[++cnt]=(edge){y,head[x],v,c};
head[x]=cnt;
e[++cnt]=(edge){x,head[y],0,-c};
head[y]=cnt;
}
bool spfa(){
queue<int>q;
memset(dis,0x3f3f3f3f,sizeof(dis));
memset(from,-1,sizeof(from));
q.push(s);
v[0]=1;dis[s]=0;
while(!q.empty()){
int u=q.front();
q.pop(),v[u]=0;
for(int i=head[u];i!=-1;i=e[i].nxt)
if(e[i].v&&dis[u]+e[i].c<dis[e[i].to]){
dis[e[i].to]=dis[u]+e[i].c;
from[e[i].to]=u,pre[e[i].to]=i;
if(!v[e[i].to]) q.push(e[i].to),v[e[i].to]=1;
}
}
return from[t]!=-1;
}
int main(){
memset(head,-1,sizeof(head));
n=read(),m=read();
s=0,t=(n<<1)+1;
cnt=-1;
for(int i=1,x;i<=n;++i){
x=read();
add(s,n+i,1,x);
add(s,i,1,0);
add(n+i,t,1,0);
}
for(int i=1,x,y,z;i<=m;++i){
x=read(),y=read(),z=read();
if(x>y) swap(x,y);
add(x,n+y,1,z);
}
int cost=0;
while(spfa()){
int f=5;
for(int i=t;i!=s;i=from[i])
f=min(f,e[pre[i]].v);
for(int i=t;i!=s;i=from[i]){
e[pre[i]].v-=f;
e[pre[i]^1].v+=f;
}
cost+=f*dis[t];
}
printf("%d\n",cost);
return 0;
}