费用流建图。
因为从小往大连边,所以不会出现环。
因为所有点都要经过,所以可以考虑它从那个点拓展而来。拆点,从源点向出点连容量1,费用为定位时间的边,向入点连容量为1,费用为0的边,从出边向汇点连容量为1,费用为0的边,对于x-->y,从x的入点向y的出点连容量为1,费用为时间的边。这样可以保证最大流的每个点都使用且最多拓展出一个点。
然后最小费用流(窝太弱,只会SPFA。。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 2000
#define M 40010
#define inf 100000000
using namespace std;
int n,m,l=1,first[N],next[M],to[M],v[M],c[M],cnt,x,y,z,q[N*2],dis[N],fa[N],Ans,fl[N];
bool flag[N];
void link(int x,int y,int V,int C)
{
to[++l]=y;v[l]=V;c[l]=C; next[l]=first[x];first[x]=l;
to[++l]=x;v[l]=0;c[l]=-C;next[l]=first[y];first[y]=l;
}
bool SPFA()
{
int head=0,tail=1;
memset(fl,0x3f,sizeof fl);
memset(flag,0,sizeof flag);
memset(dis,0x3f,sizeof dis);
q[1]=0;fl[0]=inf;dis[0]=0;
while (head!=tail)
{
head++;if (head==N) head=0;
x=q[head];flag[x]=0;
for (int i=first[x];i;i=next[i])
{
if (dis[to[i]]>dis[x]+c[i]&&v[i]>0)
{
dis[to[i]]=dis[x]+c[i];
fa[to[i]]=i;
fl[to[i]]=min(fl[x],v[i]);
if (!flag[to[i]])
{
flag[to[i]]=1;
tail++;if (tail==N) tail=0;
q[tail]=to[i];
}
}
}
}
return fl[cnt]<inf;
}
void flow()
{
for (int i=cnt;i;i=to[fa[i]^1])
{
v[fa[i]]-=fl[cnt];
v[fa[i]^1]+=fl[cnt];
Ans+=c[fa[i]]*fl[cnt];
}
}
int main()
{
scanf("%d%d",&n,&m);
cnt=n+n+1;
for (int i=1;i<=n;i++)
scanf("%d",&x),link(0,i+n,1,x);
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
if (x>y) swap(x,y);
link(x,y+n,1,z);
}
for (int i=1;i<=n;i++)
link(0,i,1,0),link(i+n,cnt,1,0);
while(SPFA()) flow();
printf("%d\n",Ans);
}