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

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44702813");
}

前言:

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

题解:

首先图转化成源点往开始图的黑点(当然你要用白点也不是不行)流流量,最终从结束图的黑点流向汇点。这个应该都能想到。

然后关键是怎么在流过一次后同时限制两个点。

这也是我所想知道的……可是,下面的方法并没有告诉我该如何实现它,它用的是分析改流量。[请允许我做一个捂脸熊的表情]

我们分析:

如果一个点是初始图上的黑点,那么它可以

最多可以流入?限制修改次数2?,最多可以流出?限制修改次数+12?

如果一个点是目标图上的黑点,那么它可以

最多可以流入?限制修改次数+12?,最多可以流出?限制修改次数2?

否则

最多可以流入?限制修改次数2?,最多可以流出?限制修改次数2?

然后我们需要特判一下如果一个点既是初始图上的黑点,又是目标图上上的黑点,那么显然它的流入流出都应该跟【否则】那一部分一样。因为流入流出以后跟正常的点一样了么。

然后费用什么乱搞就行了,想设哪就设哪(但是乱设的话最终答案可能需要/2)

注意是八连通。

代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1300
#define M 10000
#define P 25
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
using namespace std;
const int dx[]={-1,0,1,-1,1,-1,0,1};
const int dy[]={-1,-1,-1,0,0,1,1,1};
struct KSD
{
    int u,v,len,fee,next;
}e[M];
int head[N],cnt;
inline void add(int u,int v,int len,int fee)
{
    e[++cnt].u=u;
    e[cnt].v=v;
    e[cnt].len=len;
    e[cnt].fee=fee;
    e[cnt].next=head[u];
    head[u]=cnt;
}
int dist[N],s,t;
int lim[N],pre[N];
bool in[N];
queue<int>q;
void spfa()
{
    while(!q.empty())q.pop();
    memset(dist,0x3f,sizeof dist);

    q.push(s),dist[s]=0,lim[s]=inf;
    int i,u,v;
    while(!q.empty())
    {
        u=q.front(),q.pop(),in[u]=0;
        for(i=head[u];i;i=e[i].next)if(e[i].len)
        {
            if(dist[v=e[i].v]>dist[u]+e[i].fee)
            {
                dist[v]=dist[u]+e[i].fee;
                lim[v]=min(e[i].len,lim[u]);
                pre[v]=i;
                if(!in[v])q.push(v),in[v]=1;
            }
        }
    }
    return ;
}
void handle(int flow)
{
    for(int i=pre[t];i;i=pre[e[i].u])
    {
        e[i].len-=flow;
        e[i^1].len+=flow;
    }
}
int minfee,maxflow,n,m,p;
char sta[P][P],end[P][P],str[P];
int id[P][P];
bool build()
{
    int i,j,k;
    int x,y;
    scanf("%d%d",&n,&m);
    s=0,t=n*m*3+1;
    for(i=1;i<=n;i++)for(j=1;j<=m;j++)
        id[i][j]=++cnt,cnt++,cnt++;
    cnt=1;
    for(i=1;i<=n;i++)
    {
        scanf("%s",sta[i]+1);
        for(j=1;j<=m;j++)if(sta[i][j]==‘1‘)
        {
            p++;
            add(s,id[i][j]+1,1,0);
            add(id[i][j]+1,s,0,0);
        }
    }
    maxflow=p;
    for(i=1;i<=n;i++)
    {
        scanf("%s",end[i]+1);
        for(j=1;j<=m;j++)if(end[i][j]==‘1‘)
        {
            p--;
            add(id[i][j]+1,t,1,0);
            add(t,id[i][j]+1,0,0);
        }
    }
    if(p)return 1;
    for(i=1;i<=n;i++)
    {
        scanf("%s",str+1);
        for(j=1;j<=m;j++)
        {
            int flow=str[j]-‘0‘;
            if(!flow)continue;
            int flowa=(sta[i][j]==‘1‘)?(flow+1>>1):(flow>>1);
            int flowb=(end[i][j]==‘1‘)?(flow+1>>1):(flow>>1);
            if(sta[i][j]==‘1‘&&end[i][j]==‘1‘)flowa=flowb=flow>>1;

            add(id[i][j],id[i][j]+1,flowb,1);
            add(id[i][j]+1,id[i][j],0,-1);

            add(id[i][j]+1,id[i][j]+2,flowa,1);
            add(id[i][j]+2,id[i][j]+1,0,-1);
        }
        for(j=1;j<=m;j++)for(k=0;k<8;k++)
        {
            x=i+dx[k],y=j+dy[k];
            if(id[x][y])
            {
                add(id[i][j]+2,id[x][y],inf,0);
                add(id[x][y],id[i][j]+2,0,0);
            }
        }
    }
    return 0;
}
int main()
{
    freopen("test.in","r",stdin);
    if(build())
    {
        puts("-1");
        return 0;
    }
    while(spfa(),dist[t]<inf)
    {
        minfee+=lim[t]*dist[t];
        maxflow-=lim[t];
        handle(lim[t]);
    }
    if(maxflow)puts("-1");
    else cout<<(minfee>>1)<<endl;
    return 0;
}
时间: 2024-10-27 13:35:38

【BZOJ2668】【cqoi2012】交换棋子 费用流的相关文章

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&

【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行为初始状态,每行为一个

[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数字的字符串,表示每个格子参与交换的次

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,

[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 输出仅一行,为最小交换总次数.如果

[CQOI2012]交换棋子

---题面--- 题解: 其实还算是道好题 一开始很快想出了一个接近正解的建图方法,但其实是错误的,不过还是骗了70分_(:зゝ∠)_ 首先我们可以观察到棋子有限,但费用多种,其实也就相当于限制了流量,找最小费用 对于初始状态的每一个1,我们连s ---> x   flow = 1  cost = 0 对于目标状态的每一个1,我们连x ---> t  flow = 1 cost = 0 对于每一个方块,我们向周围八个格子连边 flow = inf , cost = 1(表示交换了一次) 然后就

洛谷P3159 [CQOI2012]交换棋子

巧妙的拆点方式,首先把1看成黑点,0看成空的,几次交换就可以看成一条路径 1)从容量上看,这条路径为1-2-2-2-2-2----2-1 2)从费用上看,这条路径每条边费用都是1 于是用一种巧妙的拆点方式,把一个点拆成三个,连两条边,成为一条链, 然后如果是黑点的话就由s向中间那个点连边,如果是路过的话就由一条链的尾部向另一条链的首部连边 这样就满足了上面的条件1)2) 容量的话如果是黑点出来就是(c+1)/2,进来是c/2,其他的以此类推 1 #include<cstdio> 2 #incl

【题解】CQOI2012交换棋子

感受到网络流的强大了--这道题目的关键在于: 前后颜色不变的,流入流出的次数相等:原本是黑色的最后变成了白色,流出比流入次数多1:原本是白色最后变成黑色,流入比流出次数多一.所以我们将每一点拆成3个点,分别代表流入点,原点与流出点.最开始为黑色的点与源点连流量为1,费用为0的边,最后为黑色的点与汇点连流量为1,费用为0的边. #include<bits/stdc++.h> using namespace std; #define maxn 300 #define maxm 8000 #defi

p3159 [CQOI2012]交换棋子

传送门 分析 https://www.luogu.org/blog/dedicatus545/solution-p3159 代码 #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cctype> #include<cmath> #include<cstdlib>