tsinsen A1220. 复杂的大门(陈许旻)

A1220. 复杂的大门(陈许旻)
时间限制: 1.0s   内存限制: 512.0MB  
总提交次数: 132   AC次数: 74   平均分: 67.84
试题来源
  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、m,意义见题目描述。
  第二行包含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
样例输出
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



题解:此题是一道网络流的题目。输出打开大门最少花费的钱数实际上就是求总的经过的数目-最大使用传送门的次数,而最多使用传送门的次数可以用最大流来求解。
首先拆点,把每个点拆成起点和终点,然后从源点向所有的起点连边,权值为该点所需要的经过次数,然后从终点向汇点连边,权值为该点所需要的经过次数,然后处理传送门的信息,传送门的起点向终点连边,权值为传送门最大使用的次数。这样就可以跑最大流啦。因为是二分图,所有果断选择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)); 
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值