Poj 3228 Gold Transportation【二分+最大流Dinic】

Gold Transportation
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 3281 Accepted: 1159

Description

Recently, a number of gold mines have been discovered in Zorroming State. To protect this treasure, we must transport this gold to the storehouses as quickly as possible. Suppose that the Zorroming State consists of N towns and there are M bidirectional roads among these towns. The gold mines are only discovered in parts of the towns, while the storehouses are also owned by parts of the towns. The storage of the gold mine and storehouse for each town is finite. The truck drivers in the Zorroming State are famous for their bad temper that they would not like to drive all the time and they need a bar and an inn available in the trip for a good rest. Therefore, your task is to minimize the maximum adjacent distance among all the possible transport routes on the condition that all the gold is safely transported to the storehouses.

Input

The input contains several test cases. For each case, the first line is integer N(1<=N<=200). The second line is N integers associated with the storage of the gold mine in every towns .The third line is also N integers associated with the storage of the storehouses in every towns .Next is integer M(0<=M<=(n-1)*n/2).Then M lines follow. Each line is three integers x y and d(1<=x,y<=N,0<d<=10000), means that there is a road between x and y for distance of d. N=0 means end of the input.

Output

For each case, output the minimum of the maximum adjacent distance on the condition that all the gold has been transported to the storehouses or "No Solution".

Sample Input

4
3 2 0 0
0 0 3 3
6
1 2 4
1 3 10
1 4 12
2 3 6
2 4 8
3 4 5
0

Sample Output

6

Source


题目大意:

一共有n个城市,对应n个城市有两行输入,第一行表示每个城市拥有多少金子,第二行表示每个城市可以存放多少金子。接下来一个数m,表示边的数量,接下来m行三个元素,表示u,v之间有一条路权值为w,问如果想将所有金子都放到该存放的地方,需要经过的路径中,最长的那条边(权值最大的那条边)最小是多少。


思路:


1、经典最大流判定的问题,对应这条最长边最小可能是多少,很容易理解,我们可以从1枚举到最大边权值来判定,然而我们还知道从1枚举到最大边权值的过程中,一定是呈出这样的趋势:随着枚举的值越来越大,能够走得边就越来越多,能够运到位的金子就会越来越多,那么根据这个递增性,我们可以二分查找枚举这个值,从而来优化枚举的时间复杂度。


2、那么对应当前枚举出来的值mid,建图方式如下:

①建立一个源点,将源点连入各个有金子的点,权值设定为金子的数量。

②建立一个汇点,将所有能够存放金子的点连入汇点,权值为能够存放的金子的数量。

③将图中m条无向边权值小于等于mid的边全部加入网络中,权值设定为INF。

然后对应建好的图跑一遍最大流,得到的值就是当前情况最多能够运到位的金子的数量,如果maxlfow==sum(sum表示金子的总数量)那么当前mid值就是一个可行解,继续二分。


3、判定方法如上,一直二分下去,直到不能二分为止,维护可行解的最小值即可。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
struct node
{
    int from;
    int to;
    int w;
    int next;
}e[3000000];
int bian[200*200*5][4];
int a[20000];
int b[20000];
int divv[20000];
int cur[20000];
int head[20000];
int n,m,cont,ss,tt;
void add(int from,int to,int w)
{
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
void getmap(int mid)
{
    ss=n+1;
    tt=ss+1;
    cont=0;
    memset(head,-1,sizeof(head));
    for(int i=0;i<n;i++)
    {
        if(a[i]>0)
        {
            add(ss,i+1,a[i]);
            add(i+1,ss,0);
        }
        if(b[i]>0)
        {
            add(i+1,tt,b[i]);
            add(tt,i+1,0);
        }
    }
    for(int i=0;i<m;i++)
    {
        if(bian[i][2]<=mid)
        {
            add(bian[i][0],bian[i][1],0x3f3f3f3f);
            add(bian[i][1],bian[i][0],0);
            add(bian[i][1],bian[i][0],0x3f3f3f3f);
            add(bian[i][0],bian[i][1],0);
        }
    }
}
int makedivv()
{
    memset(divv,0,sizeof(divv));
    divv[ss]=1;
    queue<int >s;
    s.push(ss);
    while(!s.empty())
    {
        int u=s.front();
        if(u==tt)return 1;
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(divv[v]==0&&w)
            {
                divv[v]=divv[u]+1;s.push(v);
            }
        }
    }
    return 0;
}
int Dfs(int u,int maxflow,int tt)
{
    if(u==tt)return maxflow;
    int ret=0;
    for(int &i=cur[u];i!=-1;i=e[i].next)
    {
        int w=e[i].w;
        int v=e[i].to;
        if(w&&divv[v]==divv[u]+1)
        {
            int f=Dfs(v,min(maxflow-ret,w),tt);
            e[i].w-=f;
            e[i^1].w+=f;
            ret+=f;
            if(ret==maxflow)return ret;
        }
    }
    return ret;
}
int Slove(int mid)
{
    getmap(mid);
    int ans=0;
    while(makedivv()==1)
    {
        memcpy(cur,head,sizeof(head));
        ans+=Dfs(ss,0x3f3f3f3f,tt);
    }
    int sum=0;
    for(int i=0;i<n;i++)
    {
        sum+=a[i];
    }
    if(sum==ans)return 1;
    else return 0;
}
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0)break;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=0;i<n;i++)
        {
            scanf("%d",&b[i]);
        }
        scanf("%d",&m);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&bian[i][0],&bian[i][1],&bian[i][2]);
        }
        int ans=-1;
        int mid;
        int l=0;
        int r=10000;
        while(r>=l)
        {
            mid=(l+r)/2;
            if(Slove(mid)==1)
            {
                ans=mid;
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        if(ans==-1)printf("No Solution\n");
        else
        printf("%d\n",ans);
    }
}






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值