Solution -「BJWC 2018」「洛谷 P4486」Kakuro
\(\mathcal{Description}\)
Link.
有一个 \(n\times m\) 的网格图,其中某些格子被主对角线划成两个三角形,称这样的格子为特殊格;初始时,除了一些障碍格,所有空格子和特殊格的两个三角形内都分别填上了数字。称一个网格合法,当且仅当:
- 对于每个特殊格左下方的三角形,若其不是障碍格,则其下方连续的空格子内数字之和为三角形内数字;
- 对于每个特殊格右上方的三角形,若其不是障碍格,则其右方连续的空格子内数字之和为三角形内数字。
为了使网格合法,你可以以一定代价将某个格子内的数 \(+1\) 或 \(-1\),修改每个格子的代价是独立的,亦有一些格子不能修改。
求最小代价。
\(n,m\le30\),每个空格子至少在一个左下非障碍的特殊格子下方,或在一个右上非障碍的特殊格子右方。
我干嘛还要再概括一遍那么长的题意 qwq。
\(\mathcal{Solution}\)
这种行和列和的限制有一种套路的网络流建图模型:代表行限制结点连向其限制格子的入点,格子入点连向格子出点用于体现一些代价之类的限制,最后格子出点连向限制该格子的代表列限制的结点。
本题,\(+1/-1\) 并不方便用边上的费用体现,对于一个初始数字为 \(v\),修改代价为 \(w\) 的空格子 \(c\),考虑如下建边:
(c',c,[0,f),w)\\
(c,c',[0,+\infty],w)
\]
其中 \((u,v,[l,r],w)\) 表示一条从 \(u\) 到 \(v\),流量限制为 \([l,r]\),费用为 \(w\) 的边。发现我们通过构造第一条边“必选”使 \(c\) 取到初始的数字 \(v\),再通过回流的 \((c,c')\) 做到 \(-1\)(在可行流里,就会体现为一个 \(c\rightarrow c'\rightarrow c\) 的回路),\(+1\) 比较简单,不在赘述。
剩下的就简单啦,所有可修改的行限制、列限制、空格子数字都可以用这种边的组合体现,建出图后跑有源汇上下界最小费用可行流即可。
最小费用可行流就是把计算可行流时,求最大流的算法换成求最小费用最大流的算法,由于如此建图不存在正向负权边,所以正确性保证。
\(\mathcal{Code}\)
/* Clearink */
#include <queue>
#include <cstdio>
#include <cassert>
#define int long long
typedef std::pair<int, int> pii;
const int MAXN = 100, INF = 0x3f3f3f3f;
int n, m, type[MAXN + 5][MAXN + 5], cnt, id[MAXN + 5][MAXN + 5];
int deg[MAXN * MAXN * 2 + 10];
pii num[MAXN + 5][MAXN + 5], cost[MAXN + 5][MAXN + 5];
inline int imin ( const int a, const int b ) { return a < b ? a : b; }
struct MaxFlowCostGraph {
static const int MAXND = MAXN * MAXN * 4 + 4, MAXEG = MAXN * MAXN * 100;
int ecnt, head[MAXND + 5], S, T, bound, curh[MAXND + 5], d[MAXND + 5];
bool inq[MAXND + 5];
struct Edge { int to, flw, cst, nxt; } graph[MAXEG * 2 + 5];
MaxFlowCostGraph (): ecnt ( 1 ) {}
inline void link ( const int s, const int t, const int f, const int w ) {
graph[++ecnt] = { t, f, w, head[s] };
head[s] = ecnt;
}
inline Edge& operator [] ( const int k ) { return graph[k]; }
inline void operator () ( int s, int t, const int f, const int w ) {
#ifdef RYBY
printf ( "%lld %lld ", s, t );
if ( f == INF ) printf ( "INF " );
else printf ( "%lld ", f );
printf ( "%lld\n", w );
#endif
link ( s, t, f, w ), link ( t, s, 0, -w );
}
inline bool spfa () {
static std::queue<int> que;
for ( int i = 0; i <= bound; ++i ) d[i] = INF, inq[i] = false;
d[S] = 0, inq[S] = true, que.push ( S );
while ( !que.empty () ) {
int u = que.front (); que.pop ();
inq[u] = false;
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( graph[i].flw && d[v = graph[i].to] > d[u] + graph[i].cst ) {
d[v] = d[u] + graph[i].cst;
if ( !inq[v] ) que.push ( v ), inq[v] = true;
}
}
}
return d[T] != INF;
}
inline pii dfs ( const int u, const int iflw ) {
if ( u == T ) return { iflw, 0 };
inq[u] = true; pii ret ( 0, 0 );
for ( int& i = curh[u], v; i; i = graph[i].nxt ) {
if ( graph[i].flw && !inq[v = graph[i].to]
&& d[v] == d[u] + graph[i].cst ) {
pii oflw ( dfs ( v, imin ( iflw - ret.first, graph[i].flw ) ) );
graph[i].flw -= oflw.first, graph[i ^ 1].flw += oflw.first;
ret.first += oflw.first;
ret.second += graph[i].cst * oflw.first + oflw.second;
if ( ret.first == iflw ) break;
}
}
if ( !ret.first ) d[u] = INF;
return inq[u] = false, ret;
}
inline pii calc ( const int s, const int t ) {
S = s, T = t;
pii ret ( 0, 0 );
while ( spfa () ) {
for ( int i = 0; i <= bound; ++i ) inq[i] = false, curh[i] = head[i];
pii tmp ( dfs ( S, INF ) );
ret.first += tmp.first, ret.second += tmp.second;
}
return ret;
}
} graph;
inline void readKakuro ( pii arr[MAXN + 5][MAXN + 5] ) {
for ( int i = 1; i <= n; ++i ) {
for ( int j = 1; j <= m; ++j ) {
arr[i][j].first = arr[i][j].second = -1;
if ( type[i][j] == 1 || type[i][j] == 4 ) {
scanf ( "%lld", &arr[i][j].first );
} else if ( type[i][j] == 2 ) {
scanf ( "%lld", &arr[i][j].second );
} else if ( type[i][j] == 3 ) {
scanf ( "%lld %lld", &arr[i][j].first, &arr[i][j].second );
}
}
}
}
inline int ident ( const int i, const int j, const bool r, const bool t = false ) {
assert ( 1 <= i && i <= n );
assert ( 1 <= j && j <= m );
assert ( type[i][j] && ( !t || ( t && ( type[i][j] == 2 || type[i][j] == 3 ) ) ) );
return r * cnt + id[i][j] + t;
}
inline void specLink ( const int s, const int t, const int f, const int c ) {
if ( ~c ) {
// (s,t,[1,f],-c) and (s,t,[0,INF],c).
graph ( s, t, INF, c );
graph ( t, s, f - 1, c ), deg[s] -= f, deg[t] += f;
} else {
deg[s] -= f, deg[t] += f;
}
}
signed main () {
scanf ( "%lld %lld", &n, &m );
for ( int i = 1; i <= n; ++i ) {
for ( int j = 1; j <= m; ++j ) {
scanf ( "%lld", &type[i][j] );
if ( type[i][j] ) id[i][j] = ++cnt, cnt += type[i][j] < 4;
}
}
int rS = cnt << 1 | 1, rT = rS + 1;
int vS = rT + 1, vT = graph.bound = vS + 1;
#ifdef RYBY
printf ( "(%lld,%lld) & (%lld,%lld)\n", rS, rT, vS, vT );
#endif
readKakuro ( num ), readKakuro ( cost );
for ( int i = 1; i <= n; ++i ) {
for ( int j = 1; j <= m; ++j ) {
if ( !type[i][j] ) continue;
if ( type[i][j] == 4 ) {
specLink ( ident ( i, j, 0 ), ident ( i, j, 1 ),
num[i][j].first, cost[i][j].first );
continue;
}
if ( ~num[i][j].first ) {
int cur = ident ( i, j, 0, 0 );
specLink ( rS, cur, num[i][j].first, cost[i][j].first );
for ( int k = i + 1; type[k][j] == 4; ++k ) {
graph ( cur, ident ( k, j, 0 ), INF, 0 );
}
}
if ( ~num[i][j].second ) {
int cur = ident ( i, j, 1, 1 );
specLink ( cur, rT, num[i][j].second, cost[i][j].second );
for ( int k = j + 1; type[i][k] == 4; ++k ) {
graph ( ident ( i, k, 1 ), cur, INF, 0 );
}
}
}
}
int req = 0;
#ifdef RYBY
puts ( "balancing degree..." );
#endif
for ( int i = 1; i <= rT; ++i ) {
if ( deg[i] > 0 ) graph ( vS, i, deg[i], 0 );
else if ( deg[i] ) req -= deg[i], graph ( i, vT, -deg[i], 0 );
}
graph ( rT, rS, INF, 0 );
pii res ( graph.calc ( vS, vT ) );
#ifdef RYBY
printf ( "req = %lld;\nres = %lld %lld.\n", req, res.first, res.second );
#endif
if ( res.first != req ) puts ( "-1" );
else printf ( "%lld\n", res.second );
return 0;
}
Solution -「BJWC 2018」「洛谷 P4486」Kakuro的更多相关文章
- 「区间DP」「洛谷P1043」数字游戏
「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...
- Solution -「JSOI 2019」「洛谷 P5334」节日庆典
\(\mathscr{Description}\) Link. 给定字符串 \(S\),求 \(S\) 的每个前缀的最小表示法起始下标(若有多个,取最小的). \(|S|\le3\time ...
- Solution -「洛谷 P4372」Out of Sorts P
\(\mathcal{Description}\) OurOJ & 洛谷 P4372(几乎一致) 设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...
- Solution -「POI 2010」「洛谷 P3511」MOS-Bridges
\(\mathcal{Description}\) Link.(洛谷上这翻译真的一言难尽呐. 给定一个 \(n\) 个点 \(m\) 条边的无向图,一条边 \((u,v,a,b)\) 表示从 ...
- Solution -「APIO 2016」「洛谷 P3643」划艇
\(\mathcal{Description}\) Link & 双倍经验. 给定 \(n\) 个区间 \([a_i,b_i)\)(注意原题是闭区间,这里只为方便后文描述),求 \(\ ...
- 「洛谷4197」「BZOJ3545」peak【线段树合并】
题目链接 [洛谷] [BZOJ]没有权限号嘤嘤嘤.题号:3545 题解 窝不会克鲁斯卡尔重构树怎么办??? 可以离线乱搞. 我们将所有的操作全都存下来. 为了解决小于等于\(x\)的操作,那么我们按照 ...
- 「洛谷3338」「ZJOI2014」力【FFT】
题目链接 [BZOJ] [洛谷] 题解 首先我们需要对这个式子进行化简,否则对着这么大一坨东西只能暴力... \[F_i=\sum_{j<i} \frac{q_iq_j}{(i-j)^2}-\s ...
- 「BZOJ2733」「洛谷3224」「HNOI2012」永无乡【线段树合并】
题目链接 [洛谷] 题解 很明显是要用线段树合并的. 对于当前的每一个连通块都建立一个权值线段树. 权值线段树处理操作中的\(k\)大的问题. 如果需要合并,那么就线段树暴力合并,时间复杂度是\(nl ...
- 「洛谷3870」「TJOI2009」开关【线段树】
题目链接 [洛谷] 题解 来做一下水题来掩饰ZJOI2019考炸的心情QwQ. 很明显可以线段树. 维护两个值,\(Lazy\)懒标记表示当前区间是否需要翻转,\(s\)表示区间还有多少灯是亮着的. ...
随机推荐
- Linux上天之路(二)之Linux安装
1. vmware workstation使用 VMware是全球领先的虚拟化公司,为客户提供虚拟化解决方案,个人虚拟化产品workstation,可以让用户通过虚拟化的方式在一台物理电脑中安装多个操 ...
- 反射获取到class文件中的实例变量
获取类的class 属性的三种方式 1.对象获取: 调用person类的父类方法getClaass(); Person p = new Person(); Class c = p.getClaass( ...
- SYCOJ2197三角形游戏
题目-三角形游戏 (shiyancang.cn) 对于任意的一个数字,把他拆分为n个数字,使得其逐层和为该数字. 首先n很小,所以很可能是搜索.DFS,n回选择. #include<bits/s ...
- Unity3D开发入门教程(三)——添加启动脚本
五邑隐侠,本名关健昌,12年游戏生涯. 本教程以 Unity 3D + VS Code + C# + tolua 为例. 一.启动脚本 第一篇 "搭建开发环境",在 "配 ...
- 【Java】枚举类
文章目录 枚举类的使用 如何定义枚举类 方式一:jdk5.0之前,自定义枚举类 方式二:jdk5.0,可以使用enum关键字定义枚举类 Enum类的主要方法 toString() values() v ...
- Docsify使用指南(打造最快捷、最轻量级的个人&团队文档)
前言 网上关于动态文档生成工具有很多如:Docsify. VuePress.Docute .Hexo这些都是一些非常优秀的文档生成工具,本章主要介绍如何快速使用Docsify搭建一个快捷.轻量级的个人 ...
- 百度云管家使用socks代理无法上传下载解决办法
像前几篇随笔描述的那样,笔者在学校里通过shadowsocks使用ipv6访问双栈vps来免费使用外网,但是在设置百度云管家的代理时发现:使用socks代理客户端可以访问文件列表,但是无法上传下载. ...
- jmeter - 阶梯式性能指标监听
概述 我们在进行阶梯式压力测试的时候,聚合报告生成的结果是一个汇总数据.并不会阶梯式的统计压测性能数据.这样我们就不能去对比不同阶梯压力下的性能数据变化趋势. 期望 假设现在一共会加载100个线程,我 ...
- 记一次redis 基于spring实现类对同一个KEY序列化内容不同导致一次事故
我们的场景是这样的 我们对一个key:比如list.point.card:1 @Resourceprivate RedisTemplate<String, Long> redisTempl ...
- golang中bufio和ioutil的使用
bufio bufio包实现了带缓冲区的读写,是对文件读写的封装 bufio缓冲写数据 模式 含义 os.O_WRONLY 只写 os.O_CREATE 创建文件 os.O_RDONLY 只读 os. ...