NOIP 2016 费用流

来源:互联网 发布:双色球数据分据库 编辑:程序博客网 时间:2024/06/06 05:00
飞扬的小鸟
【问题描述】
Flappy Bird是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机
屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果
小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。
现在小鸟们遇到了一个难题,他们遇到了一堵巨大的墙,墙上仅有m个洞供
他们通过,由于小鸟们的体型不同且墙上洞的形状也不同,所以每种体型的鸟通
过每个洞的时间都不同,鸟的体型共有n种,第i种体型的鸟通过第j个洞需要的
时间记为T(i,j),且一个洞必须前一只鸟通过之后后一只鸟才能开始通过。
从时刻0开始,鸟开始通过,而每一只鸟的等待时间为从时刻0到自己已经通
过洞的时间。现在知道了第i种体型的鸟有pi只,请求出使所有鸟都通过墙的最
少的等待时间之和。
【输入格式】
第1行包含两个正整数n和m,表示鸟的体型的种数和墙洞的数量。
第2行包含n个正整数,其中第i个数为pi,表示点第i种体型的鸟的只数。
接下来有n行,每行包含m个非负整数,这n行中的第i行的第j个数为t(i,j),
表示第i种体型的鸟通过第j个墙洞所需的时间。输入文件中每行相邻的两个数之
间均由一个空格隔开,行末均没有多余空格。
【输出格式】
输出仅一行包含一个整数,为总等待时间的最小值。
【输入输出样例】
bird.in bird.out3 2
3 1 1
5 7
3 6
8 9
47

全国信息学奥林匹克联赛(NOIP2016) 复赛模拟 提高组
5 页 共5
【数据范围】
每组数据的n、 m和p值如下:
对于 100%的数据, n<=40,m<=100,p<=800,t(i,j)<=1000(p 为鸟的总只数)
序号 n= m= p=1 5 5 102 40 1 4003 40 2 3004 40 40 405 5 40 1006 10 50 2007 20 60 4008 40 80 6009 40 100 80010 40 100 800


题解:

一个费用流的经典模型,对于这种问题,我们可以认为,对于一个洞,最后一个的鸟通
过的时间,只有他一个在等待,总答案贡献为 Time*1,倒数第二只鸟为 Time*2,一次类
推,于是我们可以建立出一张费用流的图
将源点连向 n 种鸟,费用 0,流量为这种鸟的 shuliang
将 m 个洞分别拆成 p 份,将这 m*p 个点连向汇点,费用 0,流量 1
将 n 种鸟分别连向 m*p 个洞的点,费用为 f(m,n)*1,f(m,n)*2,f(m,n)*3,……
,f(m,n)*p,流量为 1
然而 m*p 有 80000,n 有 40,连边有 3200000 条,所以需要优化,我们发现,一个洞的
f(m,n)*1 没有满流时,f(m,n)*2 一定没有用,于是我们可以一开始只将鸟连向每个洞的第
一个点,当一个点流满后,再在下一个点上建立新边,动态加边就可以过这道题了。



#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>using namespace std;typedef long long ll;const   int oo=1e9;  //无穷  const   int mm=11111111;  //边  const   int mn=888888;  //点  int node,src,dest,edge;    int ver[mm],flow[mm],cost[mm],nex[mm];    int head[mn],dis[mn],p[mn],q[mn],vis[mn];    /**这些变量基本与最大流相同,增加了   cost 表示边的费用,   p 记录可行流上节点对应的反向边   */    inline void prepare(int _node,int _src,int _dest)  //预处理   点的个数  起点  终点  {        node=_node,src=_src,dest=_dest;        for(int i=0; i<node; i++)head[i]=-1,vis[i]=0;        edge=0;    }    void addedge(int u,int v,int f,int c)    {        ver[edge]=v,flow[edge]=f,cost[edge]=c,nex[edge]=head[u],head[u]=edge++;        ver[edge]=u,flow[edge]=0,cost[edge]=-c,nex[edge]=head[v],head[v]=edge++;    }    /**以上同最大流*/    /**spfa 求最短路,并用 p 记录最短路上的边*/    bool spfa()    {        int i,u,v,l,r=0,tmp;        for(i=0; i<node; ++i)dis[i]=oo;        dis[q[r++]=src]=0;        p[src]=p[dest]=-1;        for(l=0; l!=r; (++l>=mn)?l=0:l)            for(i=head[u=q[l]],vis[u]=0; i>=0; i=nex[i])                if(flow[i]&&dis[v=ver[i]]>(tmp=dis[u]+cost[i]))                {                    dis[v]=tmp;                    p[v]=i^1;                    if(vis[v]) continue;                    vis[q[r++]=v]=1;                    if(r>=mn)r=0;                }        return p[dest]>-1;    }    int num[45],cot[45][105],n,m,sum;/**源点到汇点的一条最短路即可行流,不断的找这样的可行流*/    int SpfaFlow()    {        int i,ret=0,delta;        while(spfa())        {            for(i=p[dest],delta=oo; i>=0; i=p[ver[i]])                if(flow[i^1]<delta)delta=flow[i^1];            for(i=p[dest]; i>=0; i=p[ver[i]])                flow[i]+=delta,flow[i^1]-=delta;            ret+=delta*dis[dest];            int pt=ver[p[dest]];        int x=(pt-n-1)%sum+1,y=(pt-n+sum-1)/sum;        if(x==sum)break;        for(int i=1;i<=n;i++)addedge(i,n+(y-1)*sum+x+1,1,(x+1)*cot[i][y]);    }        return ret;    } int main(){int i,j;scanf("%d%d",&n,&m);for(i=1;i<=n;i++)scanf("%d",&num[i]),sum+=num[i];prepare(n+sum*m+2,0,n+sum*m+1);for(i=1;i<=n;i++){addedge(0,i,num[i],0);for(j=1;j<=m;j++)scanf("%d",&cot[i][j]),addedge(i,n+(j-1)*sum+1,1,cot[i][j]);}for(i=1;i<=sum;i++){for(j=1;j<=m;j++)addedge(n+(j-1)*sum+i,n+sum*m+1,1,0);}printf("%d\n",SpfaFlow());return 0;} /*3 23 1 15 73 68 947*/


原创粉丝点击