洛谷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 #include<cstdlib>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<vector>
  6 #include<queue>
  7 #define rint register int
  8 #define ll long long
  9 #define MAXN 2000+10
 10 #define pb push_back
 11 #define INF 0x7f7f7f7f
 12 #define oo 0x7f7f7f7f7f7f7f7f
 13 #define pil pair<int,ll>
 14 #define mp make_pair
 15 using namespace std;
 16 int read(){
 17     int x=0,f=1;char ch=getchar();
 18     while(ch<‘0‘||ch>‘9‘){if(‘-‘==ch)f=-1;ch=getchar();}
 19     while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
 20     return x*f;
 21 }
 22 struct E{
 23     int from,to,cap,flow;
 24     ll cost;
 25     E(int x=0,int y=0,int c=0,int f=0,ll w=0LL){
 26         from=x,to=y,cap=c,flow=f,cost=w;
 27     }
 28 };
 29 struct Dinic{
 30     int n,m,s,t;
 31     vector<E> es;
 32     vector<int> G[MAXN];
 33     void init(int n,int s,int t){
 34         this->n=n;
 35         this->s=s,this->t=t;
 36         es.clear();
 37         for(int i=0;i<=n;i++)G[i].clear();
 38     }
 39     void add(int x,int y,int cap,ll cost){
 40         es.pb(E(x,y,cap,0,cost));
 41         es.pb(E(y,x,0,0,-cost));
 42         m=es.size();
 43         G[x].pb(m-2),G[y].pb(m-1);
 44     }
 45     int p[MAXN],a[MAXN];
 46     ll d[MAXN];
 47     int b[MAXN];
 48     bool SPFA(int &flow,ll &cost){
 49         p[s]=0,a[s]=INF;
 50         memset(d,0x7f,sizeof(d));
 51         d[s]=0;
 52         memset(b,0,sizeof(b));
 53         b[s]=1;
 54         queue<int> q;
 55         q.push(s);
 56         while(!q.empty()){
 57             int x=q.front();q.pop();b[x]=0;
 58             for(rint i=0;i<G[x].size();i++){
 59                 E &e=es[G[x][i]];
 60                 if(e.cap>e.flow&&d[e.to]>d[x]+e.cost){
 61                     p[e.to]=G[x][i];
 62                     a[e.to]=min(a[x],e.cap-e.flow);
 63                     d[e.to]=d[x]+e.cost;
 64                     if(!b[e.to]){
 65                         b[e.to]=1;
 66                         q.push(e.to);
 67                     }
 68                 }
 69             }
 70         }
 71         if(oo==d[t]){
 72             return 0;
 73         }
 74         flow+=a[t];
 75         cost+=a[t]*d[t];
 76         for(rint i=t;i!=s;i=es[p[i]].from){
 77             es[p[i]].flow+=a[t];
 78             es[p[i]^1].flow-=a[t];
 79         }
 80         return 1;
 81     }
 82     pil MaxfMinc(){
 83         int flow=0;
 84         ll cost=0LL;
 85         while(SPFA(flow,cost));
 86         return mp(flow,cost);
 87     }
 88 }D;
 89 int n,m,s=0,t=MAXN-1;
 90 int id[3][25][25];
 91 int a[25][25],b[25][25],p[25][25];
 92 int cnt1=0,cnt2=0;
 93 void init(){
 94     n=read();m=read();
 95     D.init(t,s,t);
 96     char c[25];
 97     for(rint i=1;i<=n;i++){
 98         for(rint j=1;j<=m;j++){
 99             id[0][i][j]=(i-1)*m+j;
100         }
101     }
102     for(rint k=1;k<=2;k++){
103         for(rint i=1;i<=n;i++){
104             for(rint j=1;j<=m;j++){
105                 id[k][i][j]=id[k-1][i][j]+n*m;
106             }
107         }
108     }
109     for(rint i=1;i<=n;i++){
110         scanf("%s",c+1);
111         for(rint j=1;j<=m;j++){
112             a[i][j]=c[j]-‘0‘;
113         }
114     }
115     for(rint i=1;i<=n;i++){
116         scanf("%s",c+1);
117         for(rint j=1;j<=m;j++){
118             b[i][j]=c[j]-‘0‘;
119         }
120     }
121     for(rint i=1;i<=n;i++){
122         for(rint j=1;j<=m;j++){
123             if(a[i][j]&&b[i][j]){
124                 a[i][j]=b[i][j]=0;
125             }
126             else if(a[i][j]){
127                 cnt1++;
128             }
129             else if(b[i][j]){
130                 cnt2++;
131             }
132         }
133     }
134     for(rint i=1;i<=n;i++){
135         scanf("%s",c+1);
136         for(rint j=1;j<=m;j++){
137             p[i][j]=c[j]-‘0‘;
138         }
139     }
140 }
141 void add(int x,int y,int c1,int c2){
142     D.add(id[0][x][y],id[1][x][y],c1,0);
143     D.add(id[1][x][y],id[2][x][y],c2,0);
144 }
145 void solve(){
146     int dx[8]={0,0,-1,1,-1,-1,1,1};
147     int dy[8]={1,-1,0,0,-1,1,-1,1};
148     for(rint i=1;i<=n;i++){
149         for(rint j=1;j<=m;j++){
150             if(a[i][j]){
151                 add(i,j,p[i][j]>>1,(p[i][j]+1)>>1);
152                 D.add(s,id[1][i][j],1,0);
153             }
154             else if(b[i][j]){
155                 add(i,j,(p[i][j]+1)>>1,p[i][j]>>1);
156                 D.add(id[1][i][j],t,1,0);
157             }
158             else{
159                 add(i,j,(p[i][j]+1)>>1,p[i][j]>>1);
160             }
161             for(rint k=0;k<8;k++){
162                 int x=i+dx[k],y=j+dy[k];
163                 if(1<=x&&x<=n&&1<=y&&y<=m){
164                     D.add(id[2][i][j],id[0][x][y],INF,1);
165                 }
166             }
167         }
168     }
169     pil ans=D.MaxfMinc();
170     if(ans.first!=cnt1||cnt1!=cnt2){
171         printf("-1\n");
172     }
173     else{
174         printf("%d\n",ans.second);
175     }
176 }
177 int main()
178 {
179     init();
180     solve();
181     return 0;
182 }

原文地址:https://www.cnblogs.com/w-h-h/p/8453004.html

时间: 2024-10-07 00:18:52

洛谷P3159 [CQOI2012]交换棋子的相关文章

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

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>

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&

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

[CQOI2012]交换棋子

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

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,

【题解】CQOI2012交换棋子

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

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

洛谷P1466 集合 Subset Sums

洛谷P1466 集合 Subset Sums这题可以看成是背包问题 用空间为 1--n 的物品恰好填充总空间一半的空间 有几种方案 01 背包问题 1.注意因为两个交换一下算同一种方案,所以最终 要 f [ v ] / 2 2.要开 long long 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstring> 5 #include <string&g