http://acm.hdu.edu.cn/showproblem.php?pid=3338

题意:在一个n*m的地图里面,有黑方块和白方块,黑方块可能是“XXXXXXX”或者“YYY/YYY”,这里的YYY代表可能为数字,如果是在“/”左边出现数字,代表在它下面的该列的白方块的和加起来要等于这个数字,如果是在“/”右边出现数字,代表它右边的该行的白方块的和加起来要等于这个数字。我们要做的就是求出这些白方块上的数字,并按照要求输出。

思路:看完题意一脸懵逼,想了一个下午还是不知道怎么写。无奈只能看下别人的做法。

因为所有的有行数字的黑方块加起来的和等于所有的有列数字的黑方块加起来的和,所以可以把列看作是源点,把行看作是汇点,然后把有关系的白块和他们连接起来,添加一个超级源点S和列的黑方块相连,添加一个超级汇点T和行的黑方块相连,这样就可以建出图了。至于边的容量,因为是带上下限的网络流(下限是1,上限是9),为了变成没有下限的网络流,所以看作容量上下限为(0-8),这样,所以我选择对白块拆点,两点之间的容量是8,然后列的黑方块和第一个点相连,第二个点和行的黑方块相连,容量都设为INF。超级源点S与列的黑方块的容量和行的黑方块与超级汇点T的容量分别为其方块上的值减去它们对应的白方块数(因为白方块的容量-1了)。最后得到的白方块的拆点的边的容量,答案再+1就转化回来了。还有记得数组要开大点。。一开始忘了开大点T了1次。

网络流真是神奇啊!

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define N 200005
#define M 1000005
#define INF 0x3f3f3f3f
struct Edge {
int u, v, nxt, cap;
Edge () {}
Edge (int u, int v, int nxt, int cap) : u(u), v(v), nxt(nxt), cap(cap) {}
} edge[M];
struct node {
int id, cid, rid, cval, rval, type;
} mp[][];
int head[N], tot, cur[N], pre[N], gap[N], dis[N], S, T, cnt[N]; void Add(int u, int v, int cap) {
edge[tot] = Edge(u, v, head[u], cap); head[u] = tot++;
edge[tot] = Edge(v, u, head[v], ); head[v] = tot++;
} void BFS() {
queue<int> que;
memset(dis, INF, sizeof(dis));
memset(gap, , sizeof(gap));
dis[T] = ; que.push(T);
while(!que.empty()) {
int u = que.front(); que.pop();
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(dis[v] == INF) {
dis[v] = dis[u] + ;
gap[dis[v]]++;
que.push(v);
}
}
}
} int ISAP(int n) {
BFS();
memcpy(cur, head, sizeof(cur));
int u = pre[S] = S, ans = , i, flow, index;
while(dis[S] < n) {
if(u == T) {
flow = INF;
for(i = S; i != T; i = edge[cur[i]].v)
if(flow > edge[cur[i]].cap) flow = edge[cur[i]].cap, index = i;
for(i = S; i != T; i = edge[cur[i]].v)
edge[cur[i]].cap -= flow, edge[cur[i]^].cap += flow;
u = index; ans += flow;
}
for(i = cur[u]; ~i; i = edge[i].nxt) if(dis[edge[i].v] == dis[u] - && edge[i].cap > ) break;
if(~i) {
pre[edge[i].v] = u; cur[u] = i; u = edge[i].v;
} else {
int md = n + ;
if(--gap[dis[u]] == ) break;
for(i = head[u]; ~i; i = edge[i].nxt)
if(dis[edge[i].v] < md && edge[i].cap > ) md = dis[edge[i].v], cur[u] = i;
gap[dis[u] = md + ]++;
u = pre[u];
}
}
return ans;
} int main() {
int n, m;
while(~scanf("%d%d", &n, &m)) {
char s[]; tot = ; int rowcnt = , colcnt = , white = ;
memset(head, -, sizeof(head));
memset(cnt, , sizeof(cnt));
memset(mp, , sizeof(mp));
for(int i = ; i <= n; i++) {
for(int j = ; j <= m; j++) {
scanf("%s", s); int flag = , num = ;
if(strcmp(s, "XXXXXXX") == ) continue;
if(strcmp(s, ".......") == ) { mp[i][j].type = ; white++; mp[i][j].id = white; continue; }
for(int k = ; k < ; k++)
if(s[k] == 'X') { flag = ; break; }
else num = num * + s[k] - '';
if(!flag) { mp[i][j].cval = num; colcnt++; mp[i][j].cid = colcnt;}
flag = , num = ;
for(int k = ; k < ; k++)
if(s[k] == 'X') { flag = ; break; }
else num = num * + s[k] - '';
if(!flag) { mp[i][j].rval = num; rowcnt++; mp[i][j].rid = rowcnt;}
mp[i][j].type = ;
}
}
S = ; T = white * + rowcnt + colcnt + ;
for(int i = ; i <= n; i++) {
for(int j = ; j <= m; j++) {
if(mp[i][j].type == ) {
if(mp[i][j].cval > ) { // 如果黑块上列有数字
int now = mp[i][j].cid;
for(int k = i + ; k <= n; k++) { // 搜这一列上白块
if(mp[k][j].type == ) {
Add(now + white * , mp[k][j].id, INF);
mp[i][j].cval--; // 对应的列有白块,这个列容量-1
} else break; // 不是白块就退出
}
Add(S, now + white * , mp[i][j].cval);
}
if(mp[i][j].rval > ) { // 如果黑块上行有数字
int now = mp[i][j].rid;
for(int k = j + ; k <= m; k++) { // 搜这一行的白块
if(mp[i][k].type == ) {
Add(mp[i][k].id + white, now + colcnt + white * , INF);
mp[i][j].rval--; // 对应的行容量-1
} else break;
}
Add(now + colcnt + white * , T, mp[i][j].rval);
}
} else if(mp[i][j].type == ) Add(mp[i][j].id, mp[i][j].id + white, ); // 白块拆点
}
}
int ans = ISAP(T + );
for(int u = ; u <= white; u++) {
for(int i = head[u]; ~i; i = edge[i].nxt) { // 暴力搜拆点的边
if(edge[i].v == u + white) {
cnt[u] = - edge[i].cap; // 这条边的流量为 初始cap - 当前cap
}
}
}
for(int i = ; i <= n; i++) {
for(int j = ; j <= m; j++) {
if(mp[i][j].type == ) printf("%d", cnt[mp[i][j].id] + );
else putchar('_');
if(j != m) putchar(' ');
else putchar('\n');
}
}
}
return ;
}

HDU 3338:Kakuro Extension(脑洞大开的网络流)的更多相关文章

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

    HDU 3338 Kakuro Extension (网络流,最大流) Description If you solved problem like this, forget it.Because y ...

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

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

  3. HDU 3338 Kakuro Extension

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

  4. hdu 3338 最大流 ****

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

  5. HDU3338:Kakuro Extension(最大流)

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

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

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

  7. Kakuro Extension HDU - 3338 (Dinic)

    Kakuro puzzle is played on a grid of "black" and "white" cells. Apart from the t ...

  8. L - Kakuro Extension - HDU 3338 - (最大流)

    题意:有一个填数字的游戏,需要你为白色的块内填一些值,不过不能随意填的,是有一些规则的(废话),在空白的上方和作方给出一些值,如果左下角有值说明下面列的和等于这个值,右上角的值等于这行后面的数的和,如 ...

  9. 【最大流】【HDU3338】【Kakuro Extension】

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3338 题目大意:填数字,使白色区域的值得和等于有值得黑色区域的相对应的值,用网络流来做 题目思路:增加 ...

随机推荐

  1. bigdata_ Kafka集群模式部署

    环境:kafka 0.8.1.1 基本概念 Kafka维护按类区分的消息,称为主题(topic) 生产者(producer)向kafka的主题发布消息 消费者(consumer)向主题注册,并且接收发 ...

  2. WPF控件的一些特殊应用

    1 checkbox.IsChecked 返回的是bool?类型,需要用bool强转,或者直接和bool类型比较,将发生隐形转换 2 RadioButton有分组属性GroupName

  3. 基于树莓派的微型气象站设计与开发(Windows 10 IoT Core)

    前言 树莓派(Raspberry Pi,RPi)是专门为学生计算机编程教育而设计,只有信用卡大小的卡片式电脑,可以运行Linux或者Windows 10 IoT Core操作系统.本文将利用树莓派和U ...

  4. Thinkphp模板开放给第三方编辑权限时,如何禁止模板使用php代码

    首先我要吐槽一个问题:为什么在博客园发布的文章总是被其他网站采集过去,而他们采集过去后,排名比博客园还好,比如这篇文章,我把标题复制到百度搜索,结果第一页的搜索结果全部都是采集我的,而我在博客园发布的 ...

  5. wpf事件绑定,比如一个控件的左键按下事件

    <i:Interaction.Triggers> <i:EventTrigger EventName="MouseLeftButtonDown"> < ...

  6. scp 专题

    Tips:阿里云中需要使用内网ip,否则会一直阻塞Linux scp命令用于Linux之间复制文件和目录,具体如何使用这里好好介绍一下,从本地复制到远程.从远程复制到本地是两种使用方式.这里有具体举例 ...

  7. MySQL半同步复制搭建

    默认情况下,MySQL 5.5/5.6/5.7和MariaDB 10.0/10.1的复制是异步的,异步复制可以提供最佳性能,主库把binlog日志发送给从库,这一动作就结束了,并不会验证从库是否接收完 ...

  8. Jetbrains 工具集

    http://www.jetbrains.com/ PRODUCTS IntelliJ IDEA ReSharper WebStorm PhpStorm PyCharm RubyMine AppCod ...

  9. 深入解析Windows窗口创建和消息分发(三个核心问题:怎么将不同的窗口过程勾到一起,将不同的hwnd消息分发给对应的CWnd类去处理,CWnd如何简单有效的去处理消息,由浅入深,非常清楚) good

    笔记:争取不用看下面的内容,只看自己的笔记,就能记住这个流程,就算明白了: _tWinMain-->AfxWinMain,它调用四个函数: -->AfxWinInit用于做一些框架的初始化 ...

  10. OSGI资料

    http://osgi.codeplex.com/ http://www.iopenworks.com/