zoukankan      html  css  js  c++  java
  • 「清华集训 2017」无限之环

    无限之WA

    https://www.luogu.org/problemnew/show/P4003

    本题如果知道是网络流的话,其实建图不算特别神奇,但是比较麻烦。

    数据范围过大,插头dp不能处理,而且是一个网格图,考虑网络流。

    先看是不是二分图?

    每个格子只会和相邻四个格子发生关系

    所以,黑白染色正好。

    i+j为偶数左部点,i+j为奇数右部点

    不漏水是什么?

    每个管道的四个口都能和别的接好。

    S向左,右向T连口数的容量,费用为0的边

    考虑怎么能把左右连在一起。

    考虑要上下左右四个方向匹配,那么必然还要拆点(但是这些点之间是并列关系)。

    每个本点向四个分点之间考虑连边(在这里考虑旋转以及费用)

    考虑通过流量流过来表示选择旋转与否,恰当的边赋恰当的值。

    sz=1:

    自己方向是(1,0)相邻方向(1,1),对面(1,2)

    sz=2:

    直线型:自己方向(1,0),另外两个不连(因为不能转),

    非直线型:自己两个方向(1,0),每个自己方向+2(-2),连(1,1),(手动模拟一下这样选择的四种流法对应四种旋转的位置)

    sz=3:

    自己(1,0),剩下一个0位置,距离为1的向它连(1,1),距离为2的向它连(1,2)

    sz=4

    四个方向(1,0);

    (注意,右边的单向边方向和左边的完全相反)

    然后左右分点之间相邻的关系上下,左右,下上,右左,左分点连到对应的右分点。

    然后跑最小费用最大流。

    至于-1

    先判断黑格口数是不是等于白格口数

    然后如果最大流不是口数(满流)的话,就-1

    (其实数据没有-1的点23333~~~)

    这样,一条流的意义是什么?左边的某个口和右边的某个口,通过旋转或者不旋转连接在了一起,同时两个管道的需求都少了1

    由于所有边的流量都是1(除了和ST连的),所以每个口只会流出1流,减少1的需求,

    如果最后满流

    那么意味着,所有的口都满足了自己的需求,即每个口都连上了。而且每个流都合法。

    出错点:

    1.数组开小了。。。。has[4],四位二进制数,少开一位。。。(这个导致开O2之后超级厌氧,全部输出-1WA掉,一定程度上转移了查错重心。。。)

    2.提取四位二进制数的时候,习惯性地写成了while(tmp) has[++tot]=tmp%2,tmp>>=1;然鹅,最高位是0的话,没有提取完4位就break了。而且has没有memset,高位就存上了之前可能的1.。。。。导致WA死。。

    其实开始找规律一点没错。。。。但是由于while高位0的锅,以为找错了,,,最后还打了暴力判断。。。。

    代码:(得开O2)

    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #include<bits/stdc++.h>
    #define il inline
    #define reg register int
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=2000+2;
    const int inf=0x3f3f3f3f;
    int n,m,s,t;
    struct node{
        int nxt,to;
        int w,v;
    }e[(5*N+4*N+N*6)*2];
    int hd[5*N],cnt=1;
    void add(int x,int y,int w,int v){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        e[cnt].w=w;
        e[cnt].v=v;
        hd[x]=cnt;
        
        e[++cnt].nxt=hd[y];
        e[cnt].to=x;
        e[cnt].w=0;
        e[cnt].v=-v;
        hd[y]=cnt;
    }
    int incf[5*N],dis[5*N];
    int pre[5*N];
    bool vis[5*N];
    queue<int>q;
    bool spfa(){
        while(!q.empty()) q.pop();
        memset(vis,0,sizeof vis);
        memset(dis,inf,sizeof dis);
        dis[s]=0;
        incf[s]=inf;
        pre[s]=0;
        q.push(s);
        while(!q.empty()){
            int x=q.front();q.pop();
            vis[x]=0;
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                if(e[i].w&&dis[y]>dis[x]+e[i].v){
                    dis[y]=dis[x]+e[i].v;
                    pre[y]=i;
                    incf[y]=min(e[i].w,incf[x]);
                    if(!vis[y]){
                        vis[y]=1;
                        q.push(y);
                    }
                }    
            }
        }
        if(dis[t]==inf) return false;
        return true;
    }
    int ans,maxflow;
    void upda(){
        int x=t;
        while(pre[x]){
            e[pre[x]].w-=incf[t];
            e[pre[x]^1].w+=incf[t];
            x=e[pre[x]^1].to;
        }
        ans+=incf[t]*dis[t];
        maxflow+=incf[t];
        //cout<<" ans "<<ans<<" "<<maxflow<<endl;
    }
    int has[10],tot;
    int mp[N][N];
    int sz[N];
    int num(int x,int y,int k){//5 is itself
        return ((x-1)*m+y-1)*5+k;
    }
    int main(){
        rd(n);rd(m);
        s=0,t=n*m*5+1;
        for(reg i=1;i<=16;++i){
            sz[i]=sz[i>>1]+(i&1);
        }
        int le=0,ri=0;
        for(reg i=1;i<=n;++i){
            for(reg j=1;j<=m;++j){
                rd(mp[i][j]);
                if((i+j)%2==0) add(s,num(i,j,5),sz[mp[i][j]],0),le+=sz[mp[i][j]];
                else add(num(i,j,5),t,sz[mp[i][j]],0),ri+=sz[mp[i][j]];
            }
        }
        if(le!=ri){
            printf("-1");return 0;
        }
        for(reg i=1;i<=n;++i){
            for(reg j=1;j<=m;++j){
                for(reg l=1;l<=4;++l){
                    has[l]=(mp[i][j]>>(l-1))&1;
                }
                int now=num(i,j,5);
                tot=sz[mp[i][j]];
                if((i+j)%2==0){
                    switch(tot){
                        case 0:{
                            break;
                        }
                        case 1:{
                            int pos=0;
                            //cout<<" find "<<mp[i][j]<<" :: "<<has[1]<<" "<<has[2]<<" "<<has[3]<<" "<<has[4]<<endl;
                            for(reg l=1;l<=4;++l) if(has[l]) {
                                pos=l;
                            }
                            add(now,num(i,j,pos),1,0);
                            add(now,num(i,j,pos%4+1),1,1);
                            add(now,num(i,j,pos==1?4:pos-1),1,1);
                            add(now,num(i,j,(pos+2<=4)?pos+2:pos-2),1,2);
                            break;
                        }
                        case 2:{
                            if(mp[i][j]==5||mp[i][j]==10){
    //    cout<<" dkfjdf "<<endl;
                                for(reg l=1;l<=4;++l){
                                    if(has[l]) add(now,num(i,j,l),1,0);
                                }
                            }else{
                                for(reg l=1;l<=4;++l){
                                    if(has[l]) {
                                        add(now,num(i,j,l),1,0);
                                        add(num(i,j,l),num(i,j,(l+2<=4)?l+2:l-2),1,1);
                                    }
                                }
                            }
                            break;
                        }
                        case 3:{
                            for(reg l=1;l<=4;++l){
                                if(has[l]){
                                    add(now,num(i,j,l),1,0);
                                }else{
                                    add(num(i,j,(l+1)<=4?l+1:l-3),num(i,j,l),1,1);
                                    add(num(i,j,(l+3)<=4?l+3:l-1),num(i,j,l),1,1);
                                    add(num(i,j,(l+2)<=4?l+2:l-2),num(i,j,l),1,2);
                                }
                            }
                            break;
                        }
                        case 4:{
                            for(reg l=1;l<=4;++l){
                                add(now,num(i,j,l),1,0);
                            }
                            break;
                        }
                    }
                }else{
                    switch(tot){
                        case 0:{
                            break;
                        }
                        case 1:{
                            int pos=0;
                            for(reg l=1;l<=4;++l) if(has[l]){
                                 pos=l;
                            }
                            add(num(i,j,pos),now,1,0);
                            add(num(i,j,pos%4+1),now,1,1);
                            add(num(i,j,pos==1?4:pos-1),now,1,1);
                            add(num(i,j,(pos+2<=4)?pos+2:pos-2),now,1,2);
                            break;
                        }
                        case 2:{
                            if(mp[i][j]==5||mp[i][j]==10){
                                for(reg l=1;l<=4;++l){
                                    if(has[l]) add(num(i,j,l),now,1,0);
                                }
                            }else{
                                for(reg l=1;l<=4;++l){
                                    if(has[l]) {
                                        add(num(i,j,l),now,1,0);
                                        add(num(i,j,(l+2<=4)?l+2:l-2),num(i,j,l),1,1);
                                    }
                                }
                            }
                            break;
                        }
                        case 3:{
                            for(reg l=1;l<=4;++l){
                                if(has[l]){
                                    add(num(i,j,l),now,1,0);
                                }else{
                                    add(num(i,j,l),num(i,j,(l+1)<=4?l+1:l-3),1,1);
                                    add(num(i,j,l),num(i,j,(l+3)<=4?l+3:l-1),1,1);
                                    add(num(i,j,l),num(i,j,(l+2)<=4?l+2:l-2),1,2);
                                }
                            }
                            break;
                        }
                        case 4:{
                            for(reg l=1;l<=4;++l){
                                add(num(i,j,l),now,1,0);
                            }
                            break;
                        }
                    }
                }
                if((i+j)%2==0){
                    if(i>1) add(num(i,j,1),num(i-1,j,3),1,0);
                    if(i<n)    add(num(i,j,3),num(i+1,j,1),1,0);
                    if(j>1) add(num(i,j,4),num(i,j-1,2),1,0);
                    if(j<m) add(num(i,j,2),num(i,j+1,4),1,0);
                } 
            }
        }
        while(spfa()) upda();
        //cout<<" maxflow "<<maxflow<<endl;
        if(maxflow!=le){
            puts("-1");return 0;
        }
        printf("%d",ans);
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/12/14 21:08:15
    */

    总结:

    这个题的突破口的话,

    发现插头dp不行,那么网格图,可能就是网络流(数据范围也支持)

    黑白染色可以。那么考虑最终合法的结果是什么意义。然后处理好旋转连边。

    (发现没有,直线型为什么不能转?因为这样会同时转2个点!网络流没办法处理这种旋转(除非你大力讨论))

  • 相关阅读:
    python定位一组元素并打印出文本
    python+selenium自动化报告HTMLTestRunner增加饼图展示
    PyCharm链接Oracle数据库
    python+selenium自动化鼠标事件之封装
    python学习记录--默认字典defaultdict()
    python学习记录--有序字典OrderedDict()
    python学习记录--Counter()类
    python学习记录--集合
    python学习记录--字典
    python学习记录--列表
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10122508.html
Copyright © 2011-2022 走看看