IndiaHacks 2016 - Online Edition (Div. 1 + Div. 2) D - Delivery Bears,二分+最大流问题

一道不算很难的题,做了一下午,卡在了好几个傻逼傻逼傻逼的错误上!!!(写在代码里面)

题意比较容易理解,给一个图,有一个有向边(起点,终点,容量),容量就是一共能从这条路上运走多少货物,然后就是要求有x个熊,每个熊都必须搬东西,而且必须搬得一样重的东西。所以这个问题就从一个”简单的最大流”问题—–> 并不是那么容易能想明白的问题(我自己也觉得是废话)。

思路: 如样例1,其实可以只用2个熊搬2单位东西,但是必须3个熊全上,而且要搬得一样重,所以就只能是1.5. 注意这个1.5! (其实是不是就是意味着每个熊搬的东西是0.5单位)———>是不是对于每一组数据我们只要知道,每个熊能搬的最重的ans单位,答案就是ans*x ; 那么我们是不是只要找出这个ans就行了!
重点:图的转换,本来我们的容量是输入时候的cap,但是由于我们要找一个ans,所以对于这个图,我们只要让每一条边的cap 变成 cap/ans,然后我们找最大流,算出来的最大流结果>=x,就表明在这个ans下,我们可以让x只熊全工作且 满足图中的容量条件。 然后我们二分把ans增大,否则把ans减小! (实际上就是二分查找)
这里再补充一个知识点:::: 以前一直以为log(n) 也有比较大只是比O(n)小一点点,今天及时的认识了自己的这一个错误,2^64 就已经很大很大了,所以哪怕是 1e9的数 log一下都差不多=O(1)!

所以这题代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<map>
#include<vector>
#define mem(a) memset(a,0,sizeof(a))
#define pfn printf("\n")
#define sf scanf
#define pf printf
#define fr(i,n) for(int i=0;i<n;i++)
#define INF 0x7fffffff //INT_MAX
#define inf 0x3f3f3f3f //
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
using namespace std;
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
int tab[250][250];//邻接矩阵
int Tab[250][250];
int dis[250];//距源点距离,分层图
int q[2000],h,r; //BFS队列 ,首,尾
int N,M;
int ANS;//N:点数;M,边数
int vis[55],pre[100];
int BFS(){
     int i,j;
     memset(dis,-1,sizeof(dis));//以-1填充
     dis[1]=0;
     h=0;r=1;
     q[1]=1;
     while (h<r)
     {
           j=q[++h];
           for (i=1;i<=N;i++){
// printf("i=%d j=%d dis[i]=%d tab[1][2]=%d\n",i,j,dis[i],tab[1][2]);
               if (dis[i]<0 && tab[j][i]>0)
               {
                  dis[i]=dis[j]+1;
                  q[++r]=i;
               }
           }
     }

     if (dis[N]>0)
        return 1;
     else
        return 0;//汇点的DIS小于零,表明BFS不到汇点
}
//Find代表一次增广,函数返回本次增广的流量,返回0表示无法增广
int find(int x,int low)//Low是源点到现在最窄的(剩余流量最小)的边的剩余流量
{
    int i,a=0;
    if (x==N)return low;//是汇点
    for (i=1;i<=N;i++)
    if (tab[x][i] >0 //联通
     && dis[i]==dis[x]+1 //是分层图的下一层
     &&(a=find(i,min(low,tab[x][i]))))//能到汇点(a <> 0)
    {
       tab[x][i]-=a;
       tab[i][x]+=a;
       return a;//这个地方就是高明之处,因为增广路径会有多条,所以这条语句会多次返回,返回所有可以增加的流量
    }
    return 0;

}
void updata(double flow){
    mem(tab);
// printf("flow=%.10f \n",flow);
    for(int i=1;i<=N;i++)
       for(int j=1;j<=N;j++){
        tab[i][j]=Tab[i][j]/flow;
    }
}
int main() {
    int x,tans;
    //freopen("1.txt","r",stdin);
    while(~scanf("%d%d%d",&N,&M,&x)){
        mem(Tab);
        for(int i=1;i<=M;i++){
            int f,t,cap;
            scanf("%d %d %d",&f,&t,&cap);
            Tab[f][t]+=cap;
        }
        double l=0,r=1e9,mid;
        double ans=1.0/x;                 //这个初始化一开始没有写!!!
        for(int i=1;i<80;i++){
            mid=(l+r)*1.0/2;
            updata(mid);
            ANS=0;
           while(BFS()){
               while(tans=find(1,0x7fffffff))
                   ANS+=tans;        //这个地方一开始没有用tans 调用了两次find我草!找了一下午
           }
// printf(".....x%d %.10f %d %d\n",x,mid,ANS,ans);
            if(ANS>=x){
                l=mid,ans=mid;
            }
            else
                r=mid;
        }
        printf("%.10f\n",ans*x);
    }
  return 0;
}

你可能感兴趣的:(IndiaHacks 2016 - Online Edition (Div. 1 + Div. 2) D - Delivery Bears,二分+最大流问题)