【Gym】101194J Mr.Panda and TubeMaster

看到题的第一想法:上下界最大费用循环流

后来问了下同学,发现可以如下表示:
每个格子拆成入和出,
假如这个格子没有限制必须走,那么它就可以不参与匹配,也就是匹配自己即可
对图黑白染色以决定是竖进横出还是横进竖出
向相邻格子匹配权为壁的代价
然后就变成了二分图最大权匹配

#include<stdio.h>
#include<cstring> 
#include<queue>
using namespace std;
#define cint const int &
#define poi(x,y) ((x-1)*m+y)
#define Void inline void
#define N 35
#define M 2005

int s[M],tot,S,T,Q[M*20],ans,pt[M],st[M],tm[M],ts,dis[M],n,m,c[N][N],r[N][N];
bool w[N][N],vis[M];

inline int rd() {
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

struct edge{int v,c,w,n;}e[M<<4];
Void push(cint u,cint v,cint w)
{
    e[++tot]=(edge){v,1,w,s[u]};s[u]=tot;
    e[++tot]=(edge){u,0,-w,s[v]};s[v]=tot;
}

Void prepare()
{
    S=0,T=1;tot=1;ans=0;
    #define clean(a) memset(a,0,sizeof(a))
    clean(s);
    clean(w);
}

inline bool BFS()
{
    tm[S]=++ts;
    Q[1]=S;
    for (int l=1,r=1;l<=r;l++)
    {
        vis[Q[l]]=0;
        #define V e[i].v
        for (int i=s[Q[l]];i;i=e[i].n) if (e[i].c && (tm[V]<ts || dis[V]<dis[Q[l]]+e[i].w))
        {
            if (tm[V]<ts) tm[V]=ts,vis[V]=0;
            dis[V]=dis[st[V]=Q[l]]+e[pt[V]=i].w;
            if (!vis[V]) vis[Q[++r]=V]=1;
        }
    }
    if (tm[T]<ts) return 0;
    for (int i=T;i!=S;i=st[i]) e[pt[i]].c=0,e[pt[i]^1].c=1;
    ans+=dis[T];
    return 1;
}

void solve()
{
    prepare();
    n=rd(),m=rd();
    for (int i=1;i<=n;i++) for (int j=1;j<m;j++) c[i][j]=rd();
    for (int i=1;i<n;i++) for (int j=1;j<=m;j++) r[i][j]=rd();
    for (int k=rd(),u,v;k--;u=rd(),v=rd(),w[u][v]=1);
    for (int i=1;i<=n;i++) for (int j=1;j<=m;j++)
    {
        int K=poi(i,j)<<1;
        push(S,K|1,0);push(K,T,0);
        if (!w[i][j]) push(K|1,K,0);
    }
    for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (i+j&1)
    {
        if (i<n) push(poi(i,j)<<1|1,poi(i+1,j)<<1,r[i][j]);
        if (1<i) push(poi(i,j)<<1|1,poi(i-1,j)<<1,r[i-1][j]);
    }
    else
    {
        if (j<m) push(poi(i,j)<<1|1,poi(i,j+1)<<1,c[i][j]);
        if (1<j) push(poi(i,j)<<1|1,poi(i,j-1)<<1,c[i][j-1]);
    }
    bool flag=1;
    for (int i=1;i<=n*m;i++) if (!BFS()){flag=0;break;}
    if (flag) printf("%d\n",ans);
    else puts("Impossible");
}

main(){for (int T=rd(),i=1;i<=T;i++) printf("Case #%d: ",i),solve();}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值