316C Tidying Up 费用流的完美匹配

86 篇文章 0 订阅

题目链接: http://codeforces.com/gym/257279/problem/C

题意:

你有一个 n ∗ m n*m nm 的矩阵,这些方格中有数字 1 − n ∗ m / 2 1-n*m/2 1nm/2 每个数字出现两次,现在要你选出最少的方格个数,使得在这些方格内数字可以任意交换的情况下,可以实现两个相同的数字邻接。

做法:

感觉就是用图论做,就是网络流建不出图来…和队友综合一下应该就能过了…

大概就是,假设我们已经知道了最后的排列分布,它一定是某个含横竖长度为 2 2 2 的线段图,在这个图中,如果和原来的排列不同的会产生贡献。

因为 n ∗ m n*m nm 一定为偶数,我们从原点向所有的 i + j i+j i+j 奇数连边,偶数向汇点连边,每个奇数点只向四周的偶数点连边,就可以保证可以得到像上面那样结果的构图。

代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=7000;
const int maxm=100005;
const int inf=0x3f3f3f3f;
int dis[maxn],mp[85][85];
int vis[maxn],pre[maxn];
int head[maxn],cnt;
int n,m,sp,tp;
ll ans=0;
struct node{
    int to,cap,cost,next;
}e[maxm];
void add(int from,int to,int cap,int cost){
    e[cnt].to=to; e[cnt].cap=cap;
    e[cnt].cost=cost; e[cnt].next=head[from];
    head[from]=cnt++;

    e[cnt].to=from; e[cnt].cap=0;
    e[cnt].cost=-cost; e[cnt].next=head[to];
    head[to]=cnt++;
}
bool spfa(int s,int t,int &flow,int &cost){
    queue<int> q;
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    dis[s]=0;  q.push(s);
    vis[s]=1;
    int d=inf;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;
            if(e[i].cap>0&&dis[v]>dis[u]+e[i].cost){
                dis[v]=dis[u]+e[i].cost;
                pre[v]=i;
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    if(dis[t]==inf){
        return false;
    }
    for(int i=pre[t];~i;i=pre[e[i^1].to]){
        d=min(d,e[i].cap);
    }
    for(int i=pre[t];~i;i=pre[e[i^1].to]){
        e[i].cap-=d;
        e[i^1].cap+=d;
        cost+=e[i].cost*d;
    }
    flow+=d;
    return true;
}
int mcmf(int s,int t){
    int flow=0,cost=0;
    while(spfa(s,t,flow,cost)){
        //cout<<flow<<" "<<cost<<endl;
    }
    return cost;
}
int G(int x,int y){
    return (x-1)*m+y;
}
int Same(int xl,int yl,int xr,int yr){
    return mp[xl][yl]!=mp[xr][yr];
}
int main(){
    memset(head,-1,sizeof(head));
    ans=0;
    scanf("%d%d",&n,&m);
    sp=0;tp=m*n+1;
    rep(i,1,n) rep(j,1,m) scanf("%d",&mp[i][j]);

    rep(i,1,n){
        rep(j,1,m){
            if((i+j)&1){
                add(sp,G(i,j),1,0);
                if(i>1) add(G(i,j),G(i-1,j),1,Same(i,j,i-1,j));
                if(j>1) add(G(i,j),G(i,j-1),1,Same(i,j,i,j-1));
                if(j<m) add(G(i,j),G(i,j+1),1,Same(i,j,i,j+1));
                if(i<n) add(G(i,j),G(i+1,j),1,Same(i,j,i+1,j));
            }
            else add(G(i,j),tp,1,0);
        }
    }

    cout<<mcmf(sp,tp)<<endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值