【费用流】BZOJ2668 [CQOI2012]交换棋子

2人阅读 评论(0) 收藏 举报
分类:

【题目】
原题地址
去看题吧。

【题目分析】
表示第一眼看到这个题,没有任何感觉,只会爆搜。
冷静分(mo)析(yu)了一下发现,我们可以思考一下网络流什么的,然而并不会建图2333.

【解题思路】
这题的建图十分巧妙。

我们只需要把黑色棋子移动到目标位置即可,那么考虑一条合法的移动路径,开始和结尾的点只交换了一次,中间的点交换了两次.
所以我们可以把每个点拆成三个点(a,b,c).
如果这个点是原图中的黑点.
ab连流量为c[i][j]2,费用为0的边.
bc连流量为(c[i][j]+1)2,费用为0的边.
Sb连流量为1,费用为0的边.

如果这个点是新图中的黑点.
ab连流量为c[i][j]+12,费用为0的边.
bc连流量为c[i][j]2,费用为0的边.
bT连流量为1,费用为0的边.

如果这个点在新图和原图中都是白点.
ab连流量为c[i][j]2,费用为0的边.
bc连流量为c[i][j]2,费用为0的边.
对于相邻的两个点(x,y)
x.cy.a连流量为inf,费用为1的边.
跑最小费用最大流即可.

【参考代码】

#include<bits/stdc++.h>
#define id(x,y) ((x-1)*m+y)
using namespace std;

const int INF=707406378;
const int N=25;
const int M=2e6+10;
int S,T,b1,b2;
int n,m,tot,sum,flow,ans;
int head[M],dis[M],from[M];
int num[N][N],a[N][N],b[N][N],c[N][N];
char s1[N][N],s2[N][N],s3[N][N];
bool inq[M];
queue<int>q;

struct Tway
{
    int u,v,nex,w,c;
};
Tway e[M];

void add(int u,int v,int w,int c)
{
    e[++tot]=(Tway){u,v,head[u],w,c};head[u]=tot;
    e[++tot]=(Tway){v,u,head[v],0,-c};head[v]=tot;
}

void init()
{
    scanf("%d%d",&n,&m);sum=n*m;S=0;T=sum*3+1;tot=1;
    for(int i=1;i<=n;++i)
        scanf("%s",s1[i]+1);
    for(int i=1;i<=n;++i)
        scanf("%s",s2[i]+1);
    for(int i=1;i<=n;++i)
        scanf("%s",s3[i]+1);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            a[i][j]=s1[i][j]-'0';
            if(a[i][j])
                ++b1;
            b[i][j]=s2[i][j]-'0';
            if(b[i][j])
                ++b2;
            c[i][j]=s3[i][j]-'0';
            if(a[i][j] && b[i][j])
                a[i][j]=b[i][j]=0,--b1,--b2;
        }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            int now=id(i,j);num[i][j]=now;
            if(a[i][j])
            {
                add(S,now,1,0);add(sum+now,now,c[i][j]/2,0);
                add(now,2*sum+now,(c[i][j]+1)/2,0);
            }
            else
            if(b[i][j])
            {
                add(now,T,1,0);add(sum+now,now,(c[i][j]+1)/2,0);
                add(now,2*sum+now,c[i][j]/2,0);
            }
            else
            {
                add(sum+now,now,c[i][j]/2,0);
                add(now,2*sum+now,c[i][j]/2,0);
            }
        }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            if(i>1) add(2*sum+num[i][j],sum+num[i-1][j],INF,1);
            if(j>1) add(2*sum+num[i][j],sum+num[i][j-1],INF,1);
            if(i<n) add(2*sum+num[i][j],sum+num[i+1][j],INF,1);
            if(j<m) add(2*sum+num[i][j],sum+num[i][j+1],INF,1);
            if(i>1 && j>1)  add(2*sum+num[i][j],sum+num[i-1][j-1],INF,1);
            if(i<n && j<m)  add(2*sum+num[i][j],sum+num[i+1][j+1],INF,1);
            if(i>1 && j<m)  add(2*sum+num[i][j],sum+num[i-1][j+1],INF,1);
            if(i<n && j>1)  add(2*sum+num[i][j],sum+num[i+1][j-1],INF,1);
        }       
}

bool spfa()
{
    memset(dis,127/3,sizeof(dis));
    q.push(S);inq[S]=1;dis[S]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();inq[u]=0;
        for(int i=head[u];i;i=e[i].nex)
        {
            int v=e[i].v;
            if(e[i].w && dis[u]+e[i].c<dis[v])
            {
                dis[v]=dis[u]+e[i].c;from[v]=i;
                if(!inq[v])
                {
                    q.push(v);
                    inq[v]=1;
                }
            }
        }
    }
    return dis[T]!=INF;
}

void solve()
{
    if(b1==b2)
    {
        while(spfa())
        {
            int f=INF;
            for(int i=from[T];i;i=from[e[i].u])
                f=min(f,e[i].w);
            for(int i=from[T];i;i=from[e[i].u])
                e[i].w-=f,e[i^1].w+=f;
            flow+=f;ans+=f*dis[T];
        }
        if(flow!=b1)
            ans=-1;
    }
    else
        ans=-1;
    printf("%d\n",ans);
}

int main()
{
    freopen("BZOJ2668.in","r",stdin);
    freopen("BZOJ2668.out","w",stdout);

    init();
    solve();

    return 0;
}

【总结】
这种数据范围很小的题目,如果搜索和状压之类的东西做不了,可以朝网络流方面考虑。

查看评论

【BZOJ2668】【cqoi2012】交换棋子 费用流

前言: 本来以为这种双限制流量的方法很通用很好用,所以没有去写那个一个点拆成俩的奇葩做法……但是后来我发现,这种一个点拆成三个的方法没有任何意义,它只是针对了这道题的特殊性质噗。好像并不能拓展。 ...
  • Vmurder
  • Vmurder
  • 2015-03-28 16:53:22
  • 1389

bzoj2668: [cqoi2012]交换棋子

这题真是做死我了,调了一个早上。。 然后还没用弄出一个正确的图QAQ 于是下午时突发奇想,又想到了一些以为很对的东西,于是又搞错了 ORZ 感觉思路大概都对。。 注意时大概。。 但是还是,...
  • qq_36797743
  • qq_36797743
  • 2017-08-10 14:55:29
  • 130

【bzoj2668】【cqoi2012】【交换棋子】【费用流】

Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。 Inp...
  • sunshinezff
  • sunshinezff
  • 2016-04-25 10:35:27
  • 417

[BZOJ2668][CQOI2012]交换棋子(费用流)

题目: 我是超链接 题解: 能够看出来这道题目是网络流,但并不清楚这个图怎么建 这种有交换次数限制的要考虑分成两半:最多流入的数量,最多流出的数量 作为流量限制 我们对于每个节点拆成三个...
  • Blue_CuSO4
  • Blue_CuSO4
  • 2018-03-27 21:56:28
  • 9

BZOJ2668: [cqoi2012]交换棋子 费用流

Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。 Input ...
  • u011379384
  • u011379384
  • 2013-12-28 09:05:30
  • 1846

bzoj2668 [cqoi2012]交换棋子(费用流)

可以只看做把黑点移动到目标位置。对每个点的交换次数有限制。我们容易想到拆点。但是这题比较妙的地方在于拆成了三个点。把一个黑点换到目标位置上的一条路径上,除了起终点交换了一次之外,其他点都交换了两次。我...
  • Icefox_zhx
  • Icefox_zhx
  • 2018-02-14 23:00:44
  • 44

【bzoj 2668】: [cqoi2012]交换棋子

http://www.lydsy.com/JudgeOnline/problem.php?id=2668 网络流。 注意拆点的技巧 开始拆点的时候没有注意点的出入关系,产生了“传递性...
  • willinglive
  • willinglive
  • 2015-03-17 19:59:36
  • 695

2668: [cqoi2012]交换棋子

2668: [cqoi2012]交换棋子 Time Limit: 3 Sec  Memory Limit: 128 MB Submit: 1136  Solved: 423 [Submit][S...
  • CRZbulabula
  • CRZbulabula
  • 2017-02-22 19:04:10
  • 275

【CQOI2012】交换棋子(费用流)

题目大意:有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与M[i][j]M_{[i][j]}次交换。...
  • can919
  • can919
  • 2017-03-10 22:03:29
  • 205

bzoj2668 [cqoi2012]交换棋子

【题意】 n*m棋盘,每次可以交换相邻(八连通)棋子,每个格子交换次数存在上限,求将初始棋盘置换为目标棋盘的最少交换次数。 【数据范围】 n 【思路】 费用流 将每次交换看成白棋子的移动,...
  • leolyun
  • leolyun
  • 2017-01-26 21:47:26
  • 203
    个人资料
    持之以恒
    等级:
    访问量: 2万+
    积分: 811
    排名: 6万+