A1220. 复杂的大门(陈许旻)
时间限制:
1.0s 内存限制:
512.0MB
试题来源
2011中国国家集训队命题答辩
问题描述
你去找某bm玩,到了门口才发现要打开他家的大门不是一件容易的事……
他家的大门外有n个站台,用1到n的正整数编号。你需要对每个站台访问一定次数以后大门才能开启。站台之间有m个单向的传送门,通过传送门到达另一个站台不需要花费任何代价。而如果不通过传送门,你就需要乘坐公共汽车,并花费1单位的钱。值得庆幸的是,任意两个站台之间都有公共汽车直达。
现在给你每个站台必须访问的次数F i,对于站台i,你必须恰好访问F i次(不能超过)。
我们用u、v、w三个参数描述一个传送门,表示从站台u到站台v有一个最多可以使用w次的传送门(不一定要使用w次)。值得注意的是,对于任意一对传送门(u 1,v 1)和(u 2,v 2),如果有u 1<u 2,则有v 1≤v 2;如果有v 1<v 2,则有u 1≤u 2;且u 1=u 2和v 1=v 2不同时成立。
你可以从任意的站台开始,从任意的站台结束。出发去开始的站台需要花费1单位的钱。你需要求出打开大门最少需要花费多少单位的钱。
他家的大门外有n个站台,用1到n的正整数编号。你需要对每个站台访问一定次数以后大门才能开启。站台之间有m个单向的传送门,通过传送门到达另一个站台不需要花费任何代价。而如果不通过传送门,你就需要乘坐公共汽车,并花费1单位的钱。值得庆幸的是,任意两个站台之间都有公共汽车直达。
现在给你每个站台必须访问的次数F i,对于站台i,你必须恰好访问F i次(不能超过)。
我们用u、v、w三个参数描述一个传送门,表示从站台u到站台v有一个最多可以使用w次的传送门(不一定要使用w次)。值得注意的是,对于任意一对传送门(u 1,v 1)和(u 2,v 2),如果有u 1<u 2,则有v 1≤v 2;如果有v 1<v 2,则有u 1≤u 2;且u 1=u 2和v 1=v 2不同时成立。
你可以从任意的站台开始,从任意的站台结束。出发去开始的站台需要花费1单位的钱。你需要求出打开大门最少需要花费多少单位的钱。
输入格式
第一行包含两个正整数n、m,意义见题目描述。
第二行包含n个正整数,第i个数表示F i。
接下来有m行,每行有三个正整数u、v、w,表示从u到v有一个可以使用w次的传送门。
第二行包含n个正整数,第i个数表示F i。
接下来有m行,每行有三个正整数u、v、w,表示从u到v有一个可以使用w次的传送门。
输出格式
输出一行一个整数,表示打开大门最少花费的钱数。
样例输入
4 3
5 5 5 5
1 2 1
3 2 1
3 4 1
5 5 5 5
1 2 1
3 2 1
3 4 1
样例输出
17
数据规模及约定
有20%的数据满足n≤10,m≤50;对于所有的w、F
i,满足1≤w,F
i≤10。有50%的数据满足n≤1000,m≤10000。100%的数据满足1≤n≤10000,1≤m≤100000;对于所有的u、v,满足1≤u,v≤n,u≠v;对于所有的w、F
i,满足1≤w,F
i≤50000。
以上的每类数据中都存在50%的数据满足对于所有的w、F i,有w=F i=1。
时限1s
以上的每类数据中都存在50%的数据满足对于所有的w、F i,有w=F i=1。
时限1s
题解:此题是一道网络流的题目。输出打开大门最少花费的钱数实际上就是求总的经过的数目-最大使用传送门的次数,而最多使用传送门的次数可以用最大流来求解。
首先拆点,把每个点拆成起点和终点,然后从源点向所有的起点连边,权值为该点所需要的经过次数,然后从终点向汇点连边,权值为该点所需要的经过次数,然后处理传送门的信息,传送门的起点向终点连边,权值为传送门最大使用的次数。这样就可以跑最大流啦。因为是二分图,所有果断选择dinic。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
int n,m,i,j,f[10003],sum=0;
int tot,next[400003],point[30000],v[400003],remain[400003];
int deep[30000],cur[30000];
const int inf=1e9;
void add(int x,int y,int z)
{
tot++; next[tot]=point[x]; point[x]=tot; remain[tot]=z; v[tot]=y;
tot++; next[tot]=point[y]; point[y]=tot; remain[tot]=0; v[tot]=x;
}
bool bfs(int s,int t)
{
memset(deep,0x7f,sizeof(deep));
for (int i=s;i<=t;i++)
cur[i]=point[i];
deep[s]=0; queue<int> p; p.push(s);
while (!p.empty())
{
int now=p.front(); p.pop();
for (int i=point[now];i!=-1;i=next[i])
if (remain[i]&&deep[v[i]]>inf)
{
deep[v[i]]=deep[now]+1;
p.push(v[i]);
}
}
if (deep[t]>inf) return false;
else return true;
}
int dfs(int now ,int t,int limit)
{
if (!limit||now==t) return limit;
int flow=0,f;
for (int i=cur[now];i!=-1;i=next[i])
{
cur[now]=i;
if (deep[v[i]]==deep[now]+1&&(f=dfs(v[i],t,min(limit,remain[i]))))
{
flow+=f; limit-=f;
remain[i]-=f; remain[i^1]+=f;
if (!limit) break;
}
}
return flow;
}
int dinic(int s,int t)
{
int ans=0;
while (bfs(s,t))
ans+=dfs(s,t,inf);
return ans;
}
int main()
{
tot=-1; memset(next,-1,sizeof(next)); memset(point,-1,sizeof(point));
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++)
{
scanf("%d",&f[i]); sum+=f[i];
add(0,i,f[i]);
add(i+n,2*n+1,f[i]);
}
for (i=1;i<=m;i++)
{
int x,y,z; scanf("%d%d%d",&x,&y,&z);
add(x,y+n,z);
}
printf("%d",sum-dinic(0,2*n+1));
}