P3159 [CQOI2012]交换棋子
题目描述
有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。
输入格式
第一行包含两个整数n,m(1<=n, m<=20)。以下n行为初始状态,每行为一个包含m个字符的01串,其中0表示黑色棋子,1表示白色棋子。以下n行为目标状态,格式同初始状态。以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次数上限。
输出格式
输出仅一行,为最小交换总次数。如果无解,输出-1。
输入输出样例
3 3 110 000 001 000 110 100 222 222 222
4
思路
对于棋盘的初始状态和最终状态可以看做二分图的两组点,而棋子的上下左右移动可以视作边的。交换黑白点可以视作讲黑子进行移动,每次费用为1,因此我们可以进行建图,但这题十分的特殊,因为它限制了每个格子的交换次数,并且用平常的拆点,我们无法表示它是否停留在改点,因此我们将1个点拆为3个点。分别为(left,now,right),每个点从left进入,停留在now上,从right出去。但对于两条边,它所限制的交换次数该如何划分又是一个问题。我们不妨把它视作黑点移动经过的次数。
设limt表示限制数。
对于初始和结果相同的点,边的为(l,now,limt/2,1),(now,r,limt/2,1)。
对于初始为黑,结果为白的情况边为(l,now,limt/2,1),(now,r,(limt+1)/2,1)。
对于初始为白,结果为黑的情况则与之相反。
原理:如果开始为黑,结果为白,则黑点一定要移动出去,则若limt为奇数时,多余的1应该加入出边。若为偶数,则加1不会对其有影响。另一种情况与之相反。
最后将源点和汇点连入图中,跑最大流最小费用即可。
代码
#include<bits/stdc++.h> #define N 10700 #define M 107000 #define inf 1<<29 #define cnt (n*m*3) #define left(i,j) ((i-1)*m+j) #define now(i,j) (((i-1)*m+j)+n*m) #define right(i,j) (((i-1)*m+j)+(n*m<<1)) using namespace std; struct node{ int y,z,p,next; }e[M*2]; int tot=1,head[N],maxflow=0,ans=0; int n,m,s,t; void add(int x,int y,int z,int p){ e[++tot].y=y;e[tot].z=z;e[tot].p=p;e[tot].next=head[x];head[x]=tot; e[++tot].y=x;e[tot].z=0;e[tot].p=-p;e[tot].next=head[y];head[y]=tot; } int incf[N],v[N],pre[N],d[N]; bool spfa(){ queue<int> q; memset(d,0x3f,sizeof(d));// 0xcf memset(v,0,sizeof(v)); q.push(s);d[s]=0;v[s]=1; incf[s]=inf; while(q.size()){ int x=q.front();v[x]=0;q.pop(); for(int i=head[x];i;i=e[i].next){ int y=e[i].y,z=e[i].z; if(!z) continue; if(d[y]>d[x]+e[i].p){//d[y]<d[x]+e[i].p d[y]=d[x]+e[i].p; incf[y]=min(incf[x],z); pre[y]=i; if(!v[y]) v[y]=1,q.push(y); } } } if(d[t]==0x3f3f3f3f) return false;//0xcfcfcfcf return true; } void update(){ int x=t; while(x!=s){ int i=pre[x]; e[i].z-=incf[t]; e[i^1].z+=incf[t]; x=e[i^1].y; } maxflow+=incf[t]; ans+=d[t]*incf[t]; } int x1[N][N],x2[N][N],z[N][N]; int dx[9]={0,-1,-1,-1,0,0,1,1,1}; int dy[9]={0,-1,0,1,-1,1,-1,0,1}; int main() { int t1=0,t2=0,ti,tj,k;char c[30]; cin>>n>>m;s=0;t=cnt+1; for(int i=1;i<=n;i++){ scanf("%s",c); for(int j=1;j<=m;j++){ if(c[j-1]=='1'){ t1++;add(s,now(i,j),1,0); x1[i][j]=1; } } } for(int i=1;i<=n;i++){ scanf("%s",c); for(int j=1;j<=m;j++){ if(c[j-1]=='1'){ t2++;add(now(i,j),t,1,0); x2[i][j]=1; } } } if(t1!=t2){ cout<<"-1"<<endl;return 0; } for(int i=1;i<=n;i++){ scanf("%s",c); for(int j=1;j<=m;j++){ t2=c[j-1]-'0'; if(x1[i][j]==x2[i][j]) add(left(i,j),now(i,j),t2/2,0),add(now(i,j),right(i,j),t2/2,0); if(x1[i][j]&&!x2[i][j]) add(left(i,j),now(i,j),t2/2,0),add(now(i,j),right(i,j),(t2+1)/2,0); if(!x1[i][j]&&x2[i][j]) add(left(i,j),now(i,j),(t2+1)/2,0),add(now(i,j),right(i,j),t2/2,0); } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ for(k=1;k<=8;k++){ ti=i+dx[k];tj=j+dy[k]; if(ti<1||ti>n||tj<1||tj>m) continue; add(right(i,j),left(ti,tj),inf,1); } } } while(spfa()) update(); if(maxflow==t1) cout<<ans<<endl; else cout<<"-1"<<endl; return 0; }
原文地址:https://www.cnblogs.com/wangyiding2003/p/11569285.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 使用Java Api 操作HDFS
- String、StringBuffer 的使用 ,两个面试问题
- Java基础【冒泡、选择排序、二分查找】
- Java基础 【Arrays 类的使用】
- i++ 是线程安全的吗?一脸蒙逼
- Java程序员必备基础:JDK 5-15都有哪些经典新特性
- Java开发中如何自动填充SQL语句中的公共字段
- 打卡群刷题总结0918——乘积最大子数组
- 打卡群刷题总结0919——打家劫舍
- 打卡群刷题总结0920——打家劫舍 II
- 打卡群刷题总结0921——最大正方形
- 打卡群刷题总结0922——丑数 II
- 打卡群刷题总结0923——完全平方数
- 打卡群刷题总结0924——最长上升子序列
- VS2017中使用QT Chart图表