zoukankan      html  css  js  c++  java
  • J

    题解

    我们可以把每个格子拆成两个点,一个表示横向的,一个表示纵向的,相邻的格子横向和纵向连边。

    如果直接按照题意做的话,我们应当在横向和纵向的点之间连边,有限制的边设下界为1,然后跑可行流。

    或者考虑用链覆盖的思想,我们把横向点当成入点,纵向点当成出点,然后相邻的入点连向出点。

    入点和出点之间连边表示的是如果流这条边,那么这个格子不放,那么有限制的格子就不连这条边。

    源点连入点,出点连汇点,最大费用最大流。

    代码

    #include<bits/stdc++.h>
    #define N 2009
    #define M 33
    #define inf 1e9
    using namespace std;
    typedef long long ll;
    const int xi=-2e8;
    queue<int>q;
    int head[N],tot,he[M][M],zo[M][M],l[M][M],r[M][M],n,m,num;
    int dis[N],fl[N],ans,pre[N],flow;
    bool ex[M][M];
    bool vis[N];
    inline ll rd(){
        ll x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    struct edge{
        int n,to,l,f;
    }e[N*10];
    inline void add(int u,int v,int l,int f){
        e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;e[tot].f=f;
        e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=0;e[tot].f=-f;
    }
    inline bool spfa(int s,int t){
        memset(dis,-0x3f,sizeof(dis));
        q.push(s);dis[s]=0;fl[s]=2e9;
        while(!q.empty()){
            int u=q.front();q.pop();vis[u]=0;
        //    cout<<u<<" "<<dis[u]<<endl;
            for(int i=head[u];i;i=e[i].n){
                int v=e[i].to;
                if(e[i].l&&dis[v]<dis[u]+e[i].f){
                    dis[v]=dis[u]+e[i].f;pre[v]=i;fl[v]=min(fl[u],e[i].l);
                    if(!vis[v]){vis[v]=1;q.push(v);}
                }
            } 
        }
        return dis[t]>xi;
    }
    inline void calc(int s,int t){
        int x=t;
        while(x!=s){
            int i=pre[x];
            e[i].l-=fl[t];e[i^1].l+=fl[t];x=e[i^1].to;
        }
        ans+=dis[t]*fl[t];
        flow+=fl[t];
    }
    inline void solve(){
        tot=1;num=0;
        n=rd();m=rd();
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)l[i][j]=++num,r[i][j]=++num;
        for(int i=1;i<=n;++i)
            for(int j=1;j<m;++j)he[i][j]=rd();
        for(int i=1;i<n;++i)
            for(int j=1;j<=m;++j)zo[i][j]=rd();
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                if((i+j)&1){
                    if(j<m)add(l[i][j],r[i][j+1],1,he[i][j]);
                    if(j>1)add(l[i][j],r[i][j-1],1,he[i][j-1]);
                }
                else{
                    if(i>1)add(l[i][j],r[i-1][j],1,zo[i-1][j]);
                    if(i<n)add(l[i][j],r[i+1][j],1,zo[i][j]);
                }
            }
        }
        int mm=rd();
        for(int i=1;i<=mm;++i){
            int x=rd(),y=rd();
            ex[x][y]=1;
        }
        int S=0,T=num+1;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j){
                add(S,l[i][j],1,0);
                add(r[i][j],T,1,0);
                if(!ex[i][j])add(l[i][j],r[i][j],1,0);
            }
        while(spfa(S,T))calc(S,T);
        if(flow!=n*m)puts("Impossible");
        else cout<<ans<<endl;
    }
    inline void unit(){
        memset(ex,0,sizeof(ex));
        memset(head,0,sizeof(head));
        ans=0;flow=0;
    }
    int main(){
        int T=rd(),tt=0;
        while(T--){
            ++tt;
            printf("Case #%d: ",tt);
            solve();
            unit();
        }
        return 0;
    }
    
  • 相关阅读:
    最近这段时间我,想在2008 的基础上,写2011 有的工具 不知道会样,这次又机会研究ploy
    Screen 可以查找屏幕pos系类的函数
    笔记1
    Ubuntu下如何安装 tar.bz2 文件
    安装ubuntu遇到“BusyBox”问题
    android luancher 如何添加快捷方式
    转 Android 源代码结构
    修改apk图标
    Linux Ubuntu 下如何安装 .SH文件
    解放你的电源键!!不用刷机不用装软件!超简单修改搜索锁屏、HOME键唤醒~~~~~
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/11013976.html
Copyright © 2011-2022 走看看