【NOIP提高】飞扬的小鸟

Description

Flappy Bird是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。
现在小鸟们遇到了一个难题,他们遇到了一堵巨大的墙,墙上仅有m个洞供他们通过,由于小鸟们的体型不同且墙上洞的形状也不同,所以每种体型的鸟通过每个洞的时间都不同,鸟的体型共有n种,第i种体型的鸟通过第j个洞需要的时间记为T(i,j),且一个洞必须前一只鸟通过之后后一只鸟才能开始通过。
从时刻0开始,鸟开始通过,而每一只鸟的等待时间为从时刻0到自己已经通过洞的时间。现在知道了第i种体型的鸟有pi只,请求出使所有鸟都通过墙的最少的等待时间之和。

Solution

看上去就像费用流,但是由于标着个NOIP就没敢打。结果爆零。
首先很显然的是S向每种鸟连一条费用为0,容量为p[i]的边。
设鸟的个数有pp个。
如果一个洞j被一只鸟i最后一次到达,那么会对答案造成t[i][j]的影响,倒数第二个到达会造成t[i][j]*2的影响因为最后一个到达的还要多等t[i][j]的时间……
那么可以把每个洞拆成pp个点,每种鸟i都向洞j拆成的倒数第k次到达的点(拆成的第k个点)连一条费用为t[i][j]*k,容量为1的边。
然后每个洞拆成的所有点都向T连一条费用为0,容量为1的边。
然后跑一次最小费用最大流就好了。两种方法:1、zkw;2、spfa(这道题spfa比较快)

边数太多!!!

一共有100个洞,800只鸟,会拆除80000个点,40种鸟向80000个点连边会连出3200000条边,跑一跑绝对会烂掉。
考虑动态加边。
一开始每个洞只拆成一个点(注意这时向T的连边也只有这些点),然后跑一次增广,找到增广的那条路径填上的洞,然后这个洞再多拆出一个点,n中鸟向它建边,它向T连边。
这样动态加边时间就会快很多。

为什么NOI的题会被表上NOIP给我们做

NOI2012美食节

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=500007;
int i,j,k,l,n,m,ans;
int p[maxn],t[1007][1007],S,T,o,hou;
int first[maxn*2],next[maxn*2],last[maxn*2],chang[maxn*2],fan[maxn*2],cost[maxn*2],num,pp;
int bz[maxn],d[maxn],id,now[maxn],sh[maxn],data[maxn],ci;
void add(int x,int y,int z,int o){
    last[++num]=y;next[num]=first[x];first[x]=num;chang[num]=z;cost[num]=o;fan[num]=num+1;
    last[++num]=x;next[num]=first[y];first[y]=num;chang[num]=0;cost[num]=-o;fan[num]=num-1;
}
bool spfa(){
    int head,tail,now,i,j,p,q;
    fo(i,S,T)d[i]=0x7fffffff;q=d[0];
    bz[0]=1;d[0]=0;
    data[1]=0;
    head=0;tail=1;
    while(headwhile(i!=0){
            if(chang[i]&&d[last[i]]>d[now]+cost[i]){
                d[last[i]]=d[now]+cost[i];
                sh[last[i]]=i;
                if (!bz[last[i]]){
                    data[++tail]=last[i];
                    bz[last[i]]=1;
                }
            }
            i=next[i];
        }
        bz[now]=0;
    }
    if(d[T]!=q)return 1;return 0;
}
void zeng(){
    int i=sh[T],j=0x7fffffff,k=0;
    for(;i;i=sh[last[fan[i]]])
    j=min(j,chang[i]),k+=cost[i],chang[i]-=j,chang[fan[i]]+=j;;
    ans+=j*k;
   // for(i=sh[T];i;i=sh[last[fan[i]]])

    int hou=last[fan[sh[T]]]-n;
    if(hou%pp){
        hou++;
        int a=(hou-1)/pp+1,b=((hou-1)%pp+1);
        add(n+hou,T,1,0);
        fo(i,1,n){
            int v=t[i][a]*b;
            add(i,n+hou,1,v);
        }
    }
}
int main(){
//  freopen("fan.in","r",stdin);
    scanf("%d%d",&n,&m);
    fo(i,1,n){
        scanf("%d",&p[i]);
        pp+=p[i];
        add(S,i,p[i],0);
    }
    S=0,T=pp*m+n+1;
    fo(i,1,n){
        fo(j,1,m){
            scanf("%d",&t[i][j]);
        }
    }
    fo(i,1,n)fo(j,1,m)add(i,n+(j-1)*pp+1,1,t[i][j]);
    o=0;
    fo(j,1,m)add(n+(j-1)*pp+1,T,1,0);
    while(spfa())zeng(); 
    printf("%d\n",ans);
}

你可能感兴趣的:(noip,noi,网络流,zkw,费用流)