1. 程式人生 > >tsinsen A1220. 複雜的大門(陳許旻)

tsinsen A1220. 複雜的大門(陳許旻)

A1220. 複雜的大門(陳許旻) 時間限制:1.0s   記憶體限制:512.0MB   總提交次數:132   AC次數:74   平均分:67.84 試題來源   2011中國國家集訓隊命題答辯 問題描述   你去找某bm玩,到了門口才發現要開啟他家的大門不是一件容易的事……
  他家的大門外有n個站臺,用1到n的正整數編號。你需要對每個站臺訪問一定次數以後大門才能開啟。站臺之間有m個單向的傳送門,通過傳送門到達另一個站臺不需要花費任何代價。而如果不通過傳送門,你就需要乘坐公共汽車,並花費1單位的錢。值得慶幸的是,任意兩個站臺之間都有公共汽車直達。
  現在給你每個站臺必須訪問的次數Fi,對於站臺i,你必須恰好訪問Fi次(不能超過)。
  我們用u、v、w三個引數描述一個傳送門,表示從站臺u到站臺v有一個最多可以使用w次的傳送門(不一定要使用w次)。值得注意的是,對於任意一對傳送門(u1
,v1)和(u2,v2),如果有u1<u2,則有v1≤v2;如果有v1<v2,則有u1≤u2;且u1=u2和v1=v2不同時成立。
  你可以從任意的站臺開始,從任意的站臺結束。出發去開始的站臺需要花費1單位的錢。你需要求出開啟大門最少需要花費多少單位的錢。 輸入格式   第一行包含兩個正整數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;對於所有的w、Fi
,滿足1≤w,Fi≤10。有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。
  時限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)); 
}