【题目描述】
有一天,TT 要去 ABC 家。ABC 的大门外有 n 个站台,用 1 到 n 的正整数编号,TT 需要对每个站台访问恰好一定次数以后才能到 ABC 家。站台之间有 m 个单向的传送门,通过传送门到达另一个站台不需要花费任何代价。而如果不通过传送门,TT 就需要乘坐公共汽车,并花费 1 单位的钱。值得庆幸的是,任意两个站台之间都有公共汽车直达。
现在给定每个站台必须访问的次数,对于站台 i ,TT 必须恰好访问 Fi 次(不能超过)。
我们用 u,v,w 三个参数描述一个传送门,表示从站台 u 到站台 v 有一个最多可以使用 w 次的传送门(不一定要使用 w 次)。对于任意一对传送门 (u1,v1) 和 (u2,v2),如果有 u1<u2,则有v1≤v2;如果有 v1<v2 ,则有 u1≤u2;且 u1=u2 和 v1=v2 不同时成立。
TT 可以从任意的站台开始,从任意的站台结束。出发去开始的站台需要花费 1 单位的钱。现在请帮助 TT 求出打开大门最少需要花费多少单位的钱。
【输入格式】
第一行包含两个正整数 n,m,意义见题目描述。
第二行包含 n 个正整数,第 i 个数表示 Fi。
接下来有 m 行,每行有三个正整数 u,v,w,表示从 u 到 v 有一个可以使用 w 次的传送门。
【输出格式】
输出仅一行包含一个整数表示答案。
【样例输入】
4 3
5 5 5 5
1 2 1
3 2 1
3 4 1
【样例输出】
17
备注
【数据范围】
有 20% 的数据满足 n≤10,m≤50;
有 50% 的数据满足 n≤1000,m≤10000;
100% 的数据满足 1≤n≤10000,1≤m≤100000;
对于所有的 u,v,满足 1≤u,v≤n;u≠v;对于所有的 w,Fi,满足 1≤w,Fi≤50000。
以上的每类数据中都存在 50% 的数据满足对于所有的 w,Fi,有 w=Fi=1。
50分算法:由题意可得,有50%的数据是w=Fi=1,那么就可用最小路径覆盖解决,用最大匹配即可,复杂度为O(n^2)
100分算法:最大流,方法同50分算法,将每条路径的上限改为Fi,答案为∑Fi-MaxFlow,需加入当前弧优化。
贴代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
int s,t;
int cnt;
int head[MAXN],nxt[MAXN],v[MAXN],w[MAXN],depth[MAXN],cur[MAXN];
bool bfs()
{
queue<int> q;
while(!q.empty())
{
q.pop();
}
memset(depth,0,sizeof(depth));
depth[s]=1;
q.push(s);
do
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=nxt[i])
if(depth[v[i]]==0&&w[i]>0)
{
depth[v[i]]=depth[u]+1;
q.push(v[i]);
}
}while(!q.empty());
if(depth[t]==0)
return 0;
return 1;
}
int dfs(int u,int dist)
{
if(u==t)
return dist;
for(int& i=cur[u];i!=-1;i=nxt[i])
{
if(depth[v[i]]==depth[u]+1&&w[i]!=0)
{
int di=dfs(v[i],min(dist,w[i]));
if(di>0)
{
w[i]-=di;
w[i^1]+=di;
return di;
}
}
}
return 0;
}
int dinic()
{
int ans=0;
while(bfs())
{
for(int i=0;i<=t;++i)
cur[i]=head[i];
while(int d=dfs(s,INF))
ans+=d;
}
return ans;
}
void add(int a,int b,int c)
{
nxt[cnt]=head[a];
head[a]=cnt;
v[cnt]=b;
w[cnt]=c;
cnt++;
}
int main()
{
int n,m;
memset(head,-1,sizeof(head));
memset(nxt,-1,sizeof(nxt));
scanf("%d%d",&n,&m);
s=0;
t=n*2+1;
int sum=0;
for(int i=1;i<=n;++i)
{
int vis;
cin>>vis;
sum+=vis;
add(s,i,vis),add(i,s,0);
add(i+n,t,vis),add(t,i+n,0);
}
for(int i=1;i<=m;++i)
{
int x,y,z;
cin>>x>>y>>z;
add(x,y+n,z);add(y+n,x,0);
}
cout<<sum-dinic();
return 0;
}