题目比较简单,但是到处是坑,建图也有点蛋疼。先求最短路,然后拆点跑最大流。
首先是图中是双向边不是单向边,而且给出的是点权,还需要判断在最短路径上的边的两个端点的先后关系,这无疑给建图带来了许多麻烦。最重要的是开long long不然过不了几个点。
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<iostream>
#define MAXN 1010
#define INF 0x3f3f3f3f3f3fLL
using namespace std;
typedef long long LL;
inline LL Min(LL a,LL b)
{return a<b?a:b;}
struct E
{
int v,op;
LL w;
E(){}
E(int a,LL b,int c)
{v = a; w = 1LL*b; op = c;}
};
vector<E> g[MAXN];
bool inroad[MAXN];
int d[MAXN],vd[MAXN],n,m,a,b,c,s,t;
LL num[MAXN],flow;
struct Edge
{
int u,v,next;
LL w;
}edge[400010],e[400010];
int cnt,head[MAXN],p;
void add_edge(int u,int v,int w)
{
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].w = 1LL*w;
edge[cnt].next = head[u];
head[u] = cnt++;
}
LL d1[MAXN],d2[MAXN];
bool inque[MAXN];
void SPFA1()
{
memset(d1,0x3f,sizeof d1);
memset(inque,0,sizeof inque);
queue<int> Q;
Q.push(1),inque[1] = 1,d1[1] = 0;
while(!Q.empty())
{
int u = Q.front();
inque[u] = 0,Q.pop();
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(d1[u]+edge[i].w < d1[v])
{
d1[v] = d1[u]+edge[i].w;
if(!inque[v]) inque[v] = 1,Q.push(v);
}
}
}
}
void SPFA2()
{
memset(d2,0x3f,sizeof d2);
memset(inque,0,sizeof inque);
queue<int> Q;
Q.push(n),inque[n] = 1,d2[n] = 0;
while(!Q.empty())
{
int u = Q.front();
inque[u] = 0,Q.pop();
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(d2[u]+edge[i].w < d2[v])
{
d2[v] = d2[u]+edge[i].w;
if(!inque[v]) inque[v] = 1,Q.push(v);
}
}
}
}
LL aug(int i,LL augco)
{
int j,mind = t-1,sz = g[i].size();
LL augc = augco,delta;
if(i == t) return augco;
for(j = 0; j < sz; j++)
{
int v = g[i][j].v;
if(g[i][j].w)
{
if(d[i] == d[v]+1)
{
delta = Min(augc,g[i][j].w);
delta = aug(v,delta);
g[i][j].w -= delta;
g[v][g[i][j].op].w += delta;
augc -= delta;
if(d[s] >= t) return augco - augc;
if(augc == 0) break;
}
if(d[v] < mind) mind = d[v];
}
}
if(augc == augco)
{
vd[d[i]]--;
if(vd[d[i]] == 0) d[s] = t;
d[i] = mind+1;
vd[d[i]]++;
}
return augco - augc;
}
void sap()
{
flow = 0;
memset(d,0,sizeof d);
memset(vd,0,sizeof vd);
vd[0] = t;
while(d[s] < t)
flow += aug(s,INF);
}
int main()
{
memset(head,-1,sizeof head);
scanf("%d%d",&n,&m);
s = 1,t = 2*n;
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d",&a,&b,&c);
add_edge(a,b,c);
add_edge(b,a,c);
e[++p].u = a,e[p].v = b,e[p].w = 1LL*c;
}
for(int i = 1; i <= n; i++)
cin>>num[i];
num[1] = INF,num[n] = INF;
SPFA1();//两次最短路第一次求各点到起点的距离,然后是到终点的距离
SPFA2();
for(int i = 0; i < cnt; i++)
{
int u = e[i].u,v = e[i].v;
if(d1[u] > d1[v]) swap(u,v);//距离起点近的点向距离起点远的点连边
if(d1[u]+d2[v]+e[i].w == d1[n])
{
inroad[u] = 1,inroad[v] = 1;//保存在最短路上的点
g[u+n].push_back(E(v,INF,g[v].size()));
g[v].push_back(E(u+n,0,g[u+n].size()-1));
}
}
for(int i = 1; i <= n; i++)
{
if(inroad[i])//拆点
{
g[i].push_back(E(i+n,num[i],g[i+n].size()));
g[i+n].push_back(E(i,0,g[i].size()-1));
}
}
sap();
cout<<flow<<endl;
}