题目链接

  费用流,类似最小路径覆盖。

  从起点向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=,f=;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-;
ch=getchar();
}
while(isdigit(ch)){
num=num*+ch-'';
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,,-dis);
} inline int count(int i){ return i&?i+:i-; } int dis[maxn];
int pre[maxn];
bool vis[maxn];
int Start,End; int spfa(){
memset(dis,/,sizeof(dis)); dis[Start]=;
memset(pre,,sizeof(pre));
queue<int>q; q.push(Start);
while(!q.empty()){
int from=q.front(); q.pop(); vis[from]=;
//printf("%d\n",from);
for(int i=head[from];i;i=edge[i].next){
int to=edge[i].to;
if(edge[i].val<=||dis[to]<=dis[from]+edge[i].dis) continue;
dis[to]=dis[from]+edge[i].dis;
pre[to]=i;
if(vis[to]) continue;
vis[to]=; q.push(to);
}
}
if(pre[End]==) return ;
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*+;
for(int i=;i<=n;++i){
add(Start,i+n,,read());
add(Start,i,,);
add(i+n,End,,);
}
for(int i=;i<=m;++i){
int from=read(),to=read(),val=read();
if(from>to) swap(from,to);
add(from,to+n,,val);
}
int ans=;
while(){
int now=spfa();
if(now==) break;
ans+=now;
}
printf("%d",ans);
return ;
}
05-15 02:45
查看更多