[CQOI2012]交换棋子(最小费用最大流)

[CQOI2012]交换棋子(luogu)

Description

题目描述

有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,

最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。

输入格式

第一行包含两个整数n,m(1<=n, m<=20)。以下n行为初始状态,每行为一个包含m个字符的01串,

其中0表示黑色棋子,1表示白色棋子。以下n行为目标状态,格式同初始状态。

以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次数上限。

输出格式

输出仅一行,为最小交换总次数。如果无解,输出-1。

Soltion

先判初始状态和目标状态黑白棋子数是否相等,若不相等直接输出-1

忽略白色棋子,将问题看做把黑色棋子移动到目标位置

容易想到网络流,从 起点 向 每个初始状态为黑色棋子的位置(后文mid) 连一条流量为 1,费用为0 的边

从 每个目标状态为黑色棋子的位置(后文mid) 向 终点 连一条流量为 1 ,费用为0的边

问题在于对交换次数的限制,且一次交换两个位置都算多了一次交换

我们将一个位置的交换分为黑色点交换进去和交换出去两种情况

发现如果某个位置的初始状态和目标状态相同,那这个位置交换进去和交换出去的次数相等

如果不相同,

若初始状态为黑色,则交换出去的次数比交换进去多1;

若目标状态为黑色,则交换进去的次数比交换出去多1;

于是可以想到将一个位置分成3个网络流中的点,分别代表in,mid,out

根据上述分析将交换次数合法分给in-mid边和mid-out边的流量

若位置 i 可与位置 j 交换,则从out_i向in_j连一条流量为inf,费用为1的边

Code

#include <cstdio>
#include <cstdlib>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=12e2+10,M=1e6;
int head[N],nxt[M],ver[M],cost[M],edge[M],tot=1;
int s,t,n,m,a[21][21],incf[N],dis[N],maxflow,mincost,pre[N];
char be[21][21],st[21][21],ch[21][21];
int py[8][2]={{1,0},{0,1},{-1,0},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
int id(int x,int y,int inv)
{
    return (x-1)*m+y+inv*n*m;
}
void add(int u,int v,int w,int c)
{
    ver[++tot]=v,nxt[tot]=head[u],edge[tot]=w,cost[tot]=c,head[u]=tot;
    ver[++tot]=u,nxt[tot]=head[v],edge[tot]=0,cost[tot]=-c,head[v]=tot;
}
bool in[N];
bool spfa()
{
    memset(dis,0x3f,sizeof(dis));
    memset(pre,0,sizeof(pre));
    int last=dis[s];
    queue <int> q;
    dis[s]=0,q.push(s),incf[s]=1<<30;
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        in[x]=false;
        for(int i=head[x],y;i;i=nxt[i])
            if(edge[i]>0 && dis[y=ver[i]]>dis[x]+cost[i])
            {
                dis[y]=dis[x]+cost[i];
                incf[y]=min(incf[x],edge[i]);
                pre[y]=i;
                if(!in[y]) in[y]=true,q.push(y);
            }
    }
    return dis[t]!=last;
}
void update()
{
    maxflow+=incf[t],mincost+=incf[t]*dis[t];
    int x=t;
    while(x!=s)
    {
        int i=pre[x];
        edge[i]-=incf[t],edge[i^1]+=incf[t];
        x=ver[i^1];
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",be[i]+1);
    for(int i=1;i<=n;i++)
        scanf("%s",st[i]+1);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ch[i]+1);
        for(int j=1;j<=m;j++)
            a[i][j]=ch[i][j]-‘0‘;
    }
    s=0,t=n*m*3+1;
    int ans1=0,ans2=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            for(int k=0;k<8;k++)
            {
                int ni=i+py[k][0],nj=j+py[k][1];
                if(ni<1 || ni>n || nj<1 || nj>m) continue;
                add(id(i,j,2),id(ni,nj,0),1<<30,0);
            }
            if(be[i][j]==‘0‘) ans1++,add(s,id(i,j,1),1,0);
            if(st[i][j]==‘0‘) ans2++,add(id(i,j,1),t,1,0);
            if(a[i][j]==0) continue;
            if(a[i][j]%2==0 || be[i][j]==st[i][j])
            {
                add(id(i,j,0),id(i,j,1),a[i][j]/2,1);
                add(id(i,j,1),id(i,j,2),a[i][j]/2,1);
                continue;
            }
            if(be[i][j]==‘0‘)
            {
                add(id(i,j,0),id(i,j,1),a[i][j]/2,1);
                add(id(i,j,1),id(i,j,2),(a[i][j]+1)/2,1);
            }
            else
            {
                add(id(i,j,0),id(i,j,1),(a[i][j]+1)/2,1);
                add(id(i,j,1),id(i,j,2),a[i][j]/2,1);
            }
        }
    if(ans1!=ans2)
    {
        puts("-1");
        return 0;
    }
    while(spfa()) update();
    if(maxflow<ans2) puts("-1");
    else printf("%d\n",mincost/2);
    return 0;
}

原文地址:https://www.cnblogs.com/hsez-cyx/p/12407541.html

时间: 2024-11-03 22:51:43

[CQOI2012]交换棋子(最小费用最大流)的相关文章

BZOJ 2668 [cqoi2012]交换棋子 | 最小费用最大流

传送门 BZOJ 2668 题解 同时分别限制流入和流出次数,所以把一个点拆成三个:入点in(x).中间点mi(x).出点ou(x). 如果一个格子x在初始状态是黑点,则连(S, mi(x), 1, 0) 如果x在目标状态是黑点,则连(mi(x), T, 1, 0) 设x的交换次数限制是w 如果x在两种状态中颜色相同,则连(in(x), mi(x), w / 2, 0), (mi(x), ou(x), w / 2, 0) 如果x只在初始状态为黑色,则连(in(x), mi(x), w / 2,

【BZOJ-2668】交换棋子 最小费用最大流

2668: [cqoi2012]交换棋子 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1055  Solved: 388[Submit][Status][Discuss] Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. Input 第一行包含两个整数n,m(1<=n, m<=20).以下n行为初始状态,每行为一个

BZOJ 2668 交换棋子(费用流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2668 题意:有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与m[i,j]次交换. 思路: 我们将1看做要移动的数字,将0看做空白.那么若1在始末状态个数不同则无解:如某个格子始末状态均有1则这个格子的1对结果无影响,可以将其都置为0.将每个格子拆为为个点p0,p1,p2: (1)若格子初始为1,则连边:<s,p0,1,0>

[bzoj2668] [洛谷P3159] [cqoi2012] 交换棋子

Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. Input 第一行包含两个整数n,m(1<=n, m<=20).以下n行为初始状态,每行为一个包含m个字符的01串,其中0表示黑色棋子,1表示白色棋子.以下n行为目标状态,格式同初始状态.以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次数上限. Output 输出仅一行,为最小交换总次数.如果

zoj 3885 The Exchange of Items 【最小费用最大流】

The Exchange of Items Time Limit: 2 Seconds      Memory Limit: 65536 KB Bob lives in an ancient village, where transactions are done by one item exchange with another. Bob is very clever and he knows what items will become more valuable later on. So,

BZOJ2668: [cqoi2012]交换棋子

题解: 可以戳这里:http://www.cnblogs.com/zig-zag/archive/2013/04/21/3033485.html 其实自己yy一下就知道这样建图的正确性了. 感觉太神奇,居然还能拆成3个点 orzzzzzzzzzzzzzzzzzzzzzzzzz 跪跪跪跪跪跪跪跪 代码: 1 #include<cstdio> 2 3 #include<cstdlib> 4 5 #include<cmath> 6 7 #include<cstring&

【BZOJ3876】【Ahoi2014】支线剧情 有下界的最小费用最大流

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43025375"); } [BZOJ2324]营救皮卡丘 这道题也是一道有下界的最小费用最大流. 我的题解地址:http://blog.csdn.net/vmurder/article/details/41378979 这道题其实就是模板题. 我的处理

POJ 3686.The Windy&#39;s 最小费用最大流

The Windy's Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 5477   Accepted: 2285 Description The Windy's is a world famous toy factory that owns M top-class workshop to make toys. This year the manager receives N orders for toys. The ma

P3381 【模板】最小费用最大流

P3381 [模板]最小费用最大流 题目描述 如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 输入输出格式 输入格式: 第一行包含四个正整数N.M.S.T,分别表示点的个数.有向边的个数.源点序号.汇点序号. 接下来M行每行包含四个正整数ui.vi.wi.fi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi),单位流量的费用为fi. 输出格式: 一行,包含两个整数,依次为最大流量和在最大流量情况下的