HDU 3338 Kakuro Extension (网络流,最大流)

Description

If you solved problem like this, forget it.Because you need to use a completely different algorithm to solve the following one.

Kakuro puzzle is played on a grid of "black" and "white" cells. Apart from the top row and leftmost column which are entirely black, the grid has some amount of white cells which form "runs" and some amount of black cells. "Run" is a vertical or horizontal maximal one-lined block of adjacent white cells. Each row and column of the puzzle can contain more than one "run". Every white cell belongs to exactly two runs — one horizontal and one vertical run. Each horizontal "run" always has a number in the black half-cell to its immediate left, and each vertical "run" always has a number in the black half-cell immediately above it. These numbers are located in "black" cells and are called "clues".The rules of the puzzle are simple:

1.place a single digit from 1 to 9 in each "white" cell

2.for all runs, the sum of all digits in a "run" must match the clue associated with the "run"

Given the grid, your task is to find a solution for the puzzle.



Picture of the first sample input



Picture of the first sample output

Input

The first line of input contains two integers n and m (2 ≤ n,m ≤ 100) — the number of rows and columns correspondingly. Each of the next n lines contains descriptions of m cells. Each cell description is one of the following 7-character strings:

.......— "white" cell;

XXXXXXX— "black" cell with no clues;

AAA\BBB— "black" cell with one or two clues. AAA is either a 3-digit clue for the corresponding vertical run, or XXX if there is no associated vertical run. BBB is either a 3-digit clue for the corresponding horizontal run, or XXX if there is no associated horizontal run.

The first row and the first column of the grid will never have any white cells. The given grid will have at least one "white" cell.It is guaranteed that the given puzzle has at least one solution.

Output

Print n lines to the output with m cells in each line. For every "black" cell print '_' (underscore), for every "white" cell print the corresponding digit from the solution. Delimit cells with a single space, so that each row consists of 2m-1 characters.If there are many solutions, you may output any of them.

Sample Input

6 6

XXXXXXX XXXXXXX 028\XXX 017\XXX 028\XXX XXXXXXX

XXXXXXX 022\022 ....... ....... ....... 010\XXX

XXX\034 ....... ....... ....... ....... .......

XXX\014 ....... ....... 016\013 ....... .......

XXX\022 ....... ....... ....... ....... XXXXXXX

XXXXXXX XXX\016 ....... ....... XXXXXXX XXXXXXX

5 8

XXXXXXX 001\XXX 020\XXX 027\XXX 021\XXX 028\XXX 014\XXX 024\XXX

XXX\035 ....... ....... ....... ....... ....... ....... .......

XXXXXXX 007\034 ....... ....... ....... ....... ....... .......

XXX\043 ....... ....... ....... ....... ....... ....... .......

XXX\030 ....... ....... ....... ....... ....... ....... XXXXXXX

Sample Output


_ _ 5 8 9 _

_ 7 6 9 8 4

_ 6 8 _ 7 6

_ 9 2 7 4 _

_ _ 7 9 _ _


_ 1 9 9 1 1 8 6

_ _ 1 7 7 9 1 9

_ 1 3 9 9 9 3 9

_ 6 7 2 4 9 2 _

Http

HDU:https://vjudge.net/problem/HDU-3338

Source

网络流,最大流

题目大意

这是一个填数的游戏。在棋盘上有若干白格和黑格。现在要在白色的格子中填数1-9,而黑格是对白格中的数作出要求。黑格中有些没有数字,有些有一个数字,有些有两个数字。如果黑格中有形如a\b的形式,说明从这个格子向下直到碰到一个非白格或出界为止,所有白格的和为a,而从这个格子向右直到碰到一个非白格或出界为止,所有白格上的数之和为b。如果黑格中缺了一个,那么就是对这个方向无要求。任意输出一种填数方案。

解决思路

首先要理解题目意思,有些绕口。然后是用比较好的方式读入(我这里是使用两个矩阵,分别存a\b中的a和b,如果是什么都没有的黑格,置为-1,如果黑格的这一半没有,也置为-1,如果是白格,置为0,其他的则是黑格中对应位置的数),强烈建议先把输入调好后再继续。

这题如果不是在做网络流的时候做,很难想到使用网络流。

首先可以知道,所有黑格左边的数之和等于右边的数之和,同时也等于我们要填的白色格子中的数之和。那么我们可以借助这个条件,从源点连边到黑色格子左边有数的,从黑色格子右边有数的连边到汇点。因为有的黑色格子中既有左边的数又有右边的数,所以我们要拆点,拆成一个负责左边(入),一个负责右边(出)。

再就是处理白色格子。首先,这是一个有上下界的网络流最大流(填的数是1-9),我们要把其转换成0-8,那么对应的那一行的和要对应的减去它对应了多少个白色格子个数。对于列也是一样的。比如这一行三个格子对应的和是22,那么就要变成19。所以我们从所有的黑色格子中左边有数的出发,连到它对应到的同一列的所有白色格子,容量为8,再将所有的白色格子连到其对应的行的的黑色格子。注意白色格子不需要拆点。

细节有些多,要慢慢调。可以参考代码。

另:这里使用Dinic实现最大流,可以参考这篇文章

代码

网络流说明:0是源点,[1,n*m]是第一个图,即所有的白格和黑格的左数,[n*m+1,n*m*2]是所有的黑格的右数,n*m*2+1是汇点

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std; const int maxN=101*101*2;
const int maxM=maxN*100*2;
const int inf=2147483647;
//这两个define分别是把(x,y)对应成一维数组中的位置,即对于每一个格子分配的编号,第二个是把一维数组里的编号转换成二维坐标,并输出,这是为了方便调试
#define pos(x,y) ((x-1)*m+(y-1)%m+1)
#define inpos(x) '['<<x/m+(int)(x%m!=0)<<','<<(x-1)%m+1<<']' class Edge
{
public:
int u,v,flow;
}; int n,m;
int cnt;
int Head[maxN];
int Next[maxM];
Edge E[maxM];
int Mat1[101][101];//第一个矩阵,存黑格中的左数
int Mat2[101][101];//第二个矩阵,存黑格中的右数
int Ans[101][101];//答案矩阵
char str[10];
int cur[maxN];
int Q[maxN];
int depth[maxN]; void Add_Edge(int u,int v,int flow);
bool bfs();
int dfs(int u,int flow); int main()
{
while (cin>>n>>m)
{
cnt=-1;//多组数据,首先清空
memset(Head,-1,sizeof(Head));
memset(Mat1,-1,sizeof(Mat1));
memset(Mat2,-1,sizeof(Mat2));
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)//读入,同时预处理出Mat1和Mat2
{
scanf("%s",str);
if ((str[0]!='X')&&(str[0]!='.'))//如果左数存在
Mat1[i][j]=(str[0]-'0')*100+(str[1]-'0')*10+(str[2]-'0')*1;
if ((str[4]!='X')&&(str[4]!='.'))//如果右数存在
Mat2[i][j]=(str[4]-'0')*100+(str[5]-'0')*10+(str[6]-'0')*1;
if (str[0]=='.')//如果为白格
Mat1[i][j]=0;
if (str[4]=='.')//如果为白格
Mat2[i][j]=0;
}
/*
for (int i=1;i<=n;i++)//输出检查
{
for (int j=1;j<=m;j++)
cout<<Mat1[i][j]<<'/'<<Mat2[i][j]<<" ";
cout<<endl;
}
//*/
for (int i=1;i<=n;i++)//网络流建图,首先建的是黑格左数的
for (int j=1;j<=m;j++)
if (Mat1[i][j]>0)//当找到一个黑格左数存在
{
int k=i+1;//要向下扫描所有的白格,k就是横坐标
while ((Mat1[k][j]==0)&&(k<=n))//只要还是白格并且没有超出范围
{
Add_Edge(pos(i,j),pos(k,j),8);//连这个黑格左数和白格
k=k+1;
}
Add_Edge(0,pos(i,j),Mat1[i][j]-(k-i)+1);//连源点和黑格左数,注意容量是黑格左数-对应的白格数量,白格数量可以通过上面的k与i作差求出
}
for (int i=1;i<=n;i++)//然后建的是黑格右数
for (int j=1;j<=m;j++)
if (Mat2[i][j]>0)//当找到一个黑格右数存在
{
int k=j+1;//要向右扫描所有的白格,k为其纵坐标
while ((Mat2[i][k]==0)&&(k<=m))
{
Add_Edge(pos(i,k),pos(i,j)+n*m,8);
k=k+1;
}
Add_Edge(pos(i,j)+n*m,n*m*2+1,(Mat2[i][j]-(k-j)+1));//连接黑色右格与汇点,注意容量同样也是黑格右数-对应的白格数量,同时注意拆点
}
/*
for (int i=0;i<=cnt;i++)
if (E[i].flow>0)
cout<<inpos(E[i].u)<<" -> "<<inpos(E[i].v)<<" "<<E[i].flow<<endl;
//*/
int Tot=0;//求解最大流
while (bfs())
{
for (int i=0;i<=n*m*2+1;i++)
cur[i]=Head[i];
while (int di=dfs(0,inf))
Tot+=di;
}
//cout<<Tot<<endl;
for (int i=Head[0];i!=-1;i=Next[i])//从源点出发,因为0->黑格左数->白格
{
int u=E[i].v;//找到一个黑格左数点
//cout<<inpos(u)<<" "<<E[i].flow<<endl;
for (int j=Head[u];j!=-1;j=Next[j])//再从黑格左数出发,找其所有对应的白格
{
int v=E[j].v;
int x=v/m+(int)(v%m!=0);//将白格的真实横纵坐标求出来
int y=(v-1)%m+1;
if ((v!=0)&&(E[j].flow<=8)&&(Mat1[x][y]==0))
{
//cout<<inpos(v)<<" "<<E[j].flow<<endl;
Ans[x][y]=8-E[j].flow;//得到答案,8减去残量就是流量,也就是这个格上的数
}
}
//cout<<endl;
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
if (Mat1[i][j]!=0)
printf("_ ");
else
printf("%d ",Ans[i][j]+1);//注意这里要+1,因为我们求的流量是[0,8]而实际求的数是[1,9]
printf("\n");
}
}
return 0;
} void Add_Edge(int u,int v,int flow)
{
cnt++;
Next[cnt]=Head[u];
Head[u]=cnt;
E[cnt].u=u;
E[cnt].v=v;
E[cnt].flow=flow; cnt++;
Next[cnt]=Head[v];
Head[v]=cnt;
E[cnt].u=v;
E[cnt].v=u;
E[cnt].flow=0;
return;
} bool bfs()
{
memset(depth,-1,sizeof(depth));
int h=1,t=0;
Q[1]=0;
depth[0]=1;
do
{
t++;
int u=Q[t];
for (int i=Head[u];i!=-1;i=Next[i])
{
int v=E[i].v;
if ((depth[v]==-1)&&(E[i].flow>0))
{
depth[v]=depth[u]+1;
h++;
Q[h]=v;
}
}
}
while (t!=h);
if (depth[n*m*2+1]==-1)
return 0;
return 1;
} int dfs(int u,int flow)
{
if (u==n*m*2+1)
return flow;
for (int &i=cur[u];i!=-1;i=Next[i])
{
int v=E[i].v;
if ((depth[v]==depth[u]+1)&&(E[i].flow>0))
{
int di=dfs(v,min(flow,E[i].flow));
if (di>0)
{
E[i].flow-=di;
E[i^1].flow+=di;
return di;
}
}
}
return 0;
}

HDU 3338 Kakuro Extension (网络流,最大流)的更多相关文章

  1. HDU - 3338 Kakuro Extension (最大流求解方格填数)

    题意:给一个方格,每行每列都有对白色格子中的数之和的要求.每个格子中的数范围在[1,9]中.现在给出了这些要求,求满足条件的解. 分析:本题读入和建图比较恶心... 用网络流求解.建立源点S和汇点T, ...

  2. HDU 3338 Kakuro Extension

    网络最大流 TLE了两天的题目.80次Submit才AC,发现是刘汝佳白书的Dinic代码还可以优化.....瞬间无语..... #include<cstdio> #include< ...

  3. HDU3338:Kakuro Extension(最大流)

    Kakuro Extension Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  4. Kakuro Extension【最大流】

    HDU-3338 这道题真的处理起来好复杂啊,题意就是个简单的方格填数问题,但是每个白点至少放1,那么最后的可能解是怎样的呢?我们是不是要把x轴上的和y轴上的统一起来,然后就是每个点都被对应的x和y匹 ...

  5. hdu 3338 最大流 ****

    题意: 黑格子右上代表该行的和,左下代表该列下的和 链接:点我 这题可以用网络流做.以空白格为节点,假设流是从左流入,从上流出的,流入的容量为行和,流出来容量为列和,其余容量不变.求满足的最大流.由于 ...

  6. HDU3338 Kakuro Extension —— 最大流、方格填数类似数独

    题目链接:https://vjudge.net/problem/HDU-3338 Kakuro Extension Time Limit: 2000/1000 MS (Java/Others)     ...

  7. HDU 3081 Marriage Match II (网络流,最大流,二分,并查集)

    HDU 3081 Marriage Match II (网络流,最大流,二分,并查集) Description Presumably, you all have known the question ...

  8. HDU 3605 Escape (网络流,最大流,位运算压缩)

    HDU 3605 Escape (网络流,最大流,位运算压缩) Description 2012 If this is the end of the world how to do? I do not ...

  9. HDU 4289 Control (网络流,最大流)

    HDU 4289 Control (网络流,最大流) Description You, the head of Department of Security, recently received a ...

随机推荐

  1. BodeAbp前端介绍

    BodeAbp的前端可以根据自己的喜好选型,推荐React.js.angular2.js.vue.js,后续我会以react.js为例说明BodeAbp前端的一些设计思路. BodeAbp提供的前端d ...

  2. tmux使用总结

    ctrl+b +%:增加垂直分屏 ctlr+b +左右箭头: 在垂直分屏中移动 ctrl+b+c:新建窗口(不分屏) ctrl+b+数字键: 切换窗口 ctrl+b+d: 断开窗口 tmux  a : ...

  3. combox的基本应用

    easyui-combox:控件的初始化: 可以在其中进行文字的筛选功能(过滤), 动态加载数据的方法. <!DOCTYPE html><html lang="en&quo ...

  4. 《Linux内核设计与实现》 第五章学习笔记

    第五章 系统调用 在现代操作系统中,内核提供了进程与内核进行交互的一组接口.有如下作用: 让应用程序受限的访问硬件设备 提供了创新进程并与已有进程进行通信的机制 提供了申请操作系统其它资源的能力 保证 ...

  5. 关于singleton的几个实现

    public class Singleton { public static void main(String[] args) { Singleton s1 = Singleton.getInstan ...

  6. 关于RESTful 的概念

    1.REST 是面向资源的,这个概念非常重要,而资源是通过 URI 进行暴露.URI 的设计只要负责把资源通过合理方式暴露出来就可以了.对资源的操作与它无关,操作是通过 HTTP动词来体现,所以RES ...

  7. mysql三级连查,左连

    需求:比如:学校里班级,班级里有学生.利用左连查出所有的信息 select <include refid="Base_Column_List_Left_Join"/>f ...

  8. Spring框架最简单的定时任务调用

    package org.jeecgframework.core.timer; import org.springframework.scheduling.annotation.Scheduled; i ...

  9. ERROR 2003: Can't connect to MySQL server on 'host ip'(10060)

    https://forums.mysql.com/read.php?51,99347,99358 https://dev.mysql.com/doc/refman/5.7/en/can-not-con ...

  10. python3_列表、元组、集合、字典

    列表list #列表的基本操作 >>> a=[] #创建空列表 >>> a = [0,1,2,3,4,5] #创建列表并初始化,列表是[]包含由逗号分隔的多个元素组 ...