题目大意:
给定n个点和m条边,建造每一个点有花费,建造每一条边有收益,但是一条边的建造必须要两个点都建造好才可以,求最大净收入。
思路:
把边和点都看成是点,边的的权值为正数,点的权值为负数,同时表示边的点向它的两个端点连都连一条有向边,要求选定一个集合满足这个集合不向外面有出边,求集合的最大权值和,就变成了最大权闭合子图问题,用网络流最小割解决。
/*=======================
* Author : ylsoi
* Problem : bzoj1497
* Algorithm : Flow
* Time : 2018.6.23
* =====================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
#define inf INT_MAX
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj1497.in","r",stdin);
freopen("bzoj1497.out","w",stdout);
}
const int maxn=5000+10;
const int maxm=50000+10;
int n,m,ss,tt,p[maxn],a[maxm],b[maxm],c[maxm],ans,tot;
int to[maxm<<3],last[maxm<<3],flow[maxm<<3],beg[maxn+maxm],cnte=1;
void add(int u,int v,int f){
last[++cnte]=beg[u];
beg[u]=cnte;
to[cnte]=v;
flow[cnte]=f;
}
void init(){
scanf("%d%d",&n,&m);
REP(i,1,n)scanf("%d",&p[i]);
REP(i,1,m)scanf("%d%d%d",&a[i],&b[i],&c[i]),tot+=c[i];
ss=0;tt=n+m+1;
REP(i,1,m)add(ss,i,c[i]), add(i,ss,0);
REP(i,1,n)add(m+i,tt,p[i]), add(tt,m+i,0);
REP(i,1,m)add(i,m+a[i],inf), add(m+a[i],i,0), add(i,m+b[i],inf), add(m+b[i],i,0);
}
struct dinic{
int cur[maxn+maxm],num[maxn+maxm];
bool bfs(){
memset(num,0,sizeof(num));
queue<int>qu;
qu.push(ss);
num[ss]=1;
while(qu.size()){
int u=qu.front();
qu.pop();
if(u==tt)return true;
for(int i=beg[u];i;i=last[i]){
if(num[to[i]] || !flow[i])continue;
qu.push(to[i]);
num[to[i]]=num[u]+1;
}
}
return false;
}
int dfs(int u,int gap){
if(u==tt || !gap)return gap;
int sum=0,f;
for(int &i=cur[u];i;i=last[i]){
if(num[to[i]]!=num[u]+1)continue;
if((f=dfs(to[i],min(gap,flow[i])))){
flow[i]-=f;
flow[i^1]+=f;
sum+=f;
gap-=f;
}
if(!gap)break;
}
return sum;
}
void work(){
while(bfs()){
REP(i,ss,tt)cur[i]=beg[i];
ans+=dfs(ss,inf);
}
}
}T;
int main(){
File();
init();
T.work();
printf("%d\n",tot-ans);
return 0;
}