NOIP模拟 Snow

【题目描述】

       有一天,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;
}

猜你喜欢

转载自blog.csdn.net/g21glf/article/details/81809432