【bzoj3171】[Tjoi2013]循环格
题目描述:
一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子。每个元素有一个坐标(行,列),其中左上角元素坐标为(0,0)。给定一个起始位置(r,c)
,你可以沿着箭头防线在格子间行走。即如果(r,c)是一个左箭头,那么走到(r,c-1);如果是右箭头那么走到(r,c+1);如果是上箭头那么走到(r-1,c);如果是下箭头那么走到(r+1,c);每一行和每一列都是循环的,即如果走出边界,你会出现在另一侧。
一个完美的循环格是这样定义的:对于任意一个起始位置,你都可以i沿着箭头最终回到起始位置。如果一个循环格不满足完美,你可以随意修改任意一个元素的箭头直到完美。给定一个循环格,你需要计算最少需要修改多少个元素使其完美。
输入:
第一行两个整数R,C。表示行和列,接下来R行,每行C个字符LRUD,表示左右上下。
输出:
一个整数,表示最少需要修改多少个元素使得给定的循环格完美
样例输入:
3 4
RRRD
URLL
LRRR
样例输出:
2
题解:
这题有两种构图方法。首先,两种方法根据出入度都为1来构图。
第一种:
(1)将所有的点拆成两个点,将一个点连向源点S,另一个点连向汇点T,连的边都是容量为1,费用为0的。代表了每个点的出入度都是1 。
(2)一个点向四周的另一类点连出一条容量为1费用为0或1(如果无需修改就是0,否则需要修改就是1)的边。
(3)跑最小费用最大流
第二种:
(1)拆点同上,但连的边的容量为该点原本的入(出)度。
(2)对于每个方格(i, j),我们假设原本连的点为(x,y),那么把这个连向的点的第二类点向其他三个没连的点的第一类点连边,容量为1,费用为1。每个第一类点和第二类点之间都连一条容量为1,费用为0的边。
(3)同第一种
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#ifdef WIN32
#define LL "%I64d"
#else
#define LL "%lld"
#endif
#ifdef CT
#define debug(...) printf(__VA_ARGS__)
#define setfile()
#else
#define debug(...)
#define filename ""
#define setfile() freopen(filename".in", "r", stdin); freopen(filename".out", "w", stdout);
#endif
#define R register
#define getc() (S == T && (T = (S = B) + fread(B, 1, 1 << 15, stdin), S == T) ? EOF : *S++)
#define dmax(_a, _b) ((_a) > (_b) ? (_a) : (_b))
#define dmin(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0)
#define cmin(_a, _b) (_a > (_b) ? _a = (_b) : 0)
char B[1 << 15], *S = B, *T = B;
inline int FastIn()
{
R char ch; R int cnt = 0; R bool minus = 0;
while (ch = getc(), (ch < '0' || ch > '9') && ch != '-') ;
ch == '-' ? minus = 1 : cnt = ch - '0';
while (ch = getc(), ch >= '0' && ch <= '9') cnt = cnt * 10 + ch - '0';
return minus ? -cnt : cnt;
}
#define maxn 20
#define maxcnt 1010
#define maxm 100010
struct Edge
{
int from, to, w, c;
Edge *next, *rev;
}*last[maxcnt], *prev[maxcnt], e[maxm], *ecnt = e;
int opt[maxn][maxn], id[maxn][maxn], s, t, ans;
int dis[maxcnt];
bool vis[maxcnt];
const int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
#define cmod(_a, _b) ((_a) % (_b) == 0 ? (_b) : (_a) % (_b))
inline void link(R int _a, R int _b, R int _w, R int _c)
{
*++ecnt = (Edge) {_a, _b, _w, _c, last[_a], ecnt + 1}; last[_a] = ecnt;
*++ecnt = (Edge) {_b, _a, 0, -_c, last[_b], ecnt - 1}; last[_b] = ecnt;
}
#define INF 23333333
std::queue <int> q;
inline bool spfa()
{
for (R int i = 0; i <= t; ++i) dis[i] = INF;
dis[s] = 0; vis[s] = 1; q.push(s);
while (!q.empty())
{
R int now = q.front(); q.pop();
for (R Edge *iter = last[now]; iter; iter = iter -> next)
{
if (iter -> w && iter -> c + dis[now] < dis[iter -> to])
{
dis[iter -> to] = iter -> c + dis[now];
prev[iter -> to] = iter;
if (!vis[iter -> to])
{
vis[iter -> to] = 1;
q.push(iter -> to);
}
}
}
vis[now] = 0;
}
return dis[t] != INF;
}
inline void mcmf()
{
R int x = INF;
for (R Edge *iter = prev[t]; iter; iter = prev[iter -> from])
cmin(x, iter -> w);
for (R Edge *iter = prev[t]; iter; iter = prev[iter -> from])
{
ans += x * iter -> c;
iter -> w -= x;
iter -> rev -> w += x;
}
}
int main()
{
R int n = FastIn(), m = FastIn(), cnt = 0;
for (R int i = 1; i <= n; ++i)
for (R int j = 1; j <= m; ++j)
{
id[i][j] = ++cnt;
R char ch;
while (ch = getc(), ch < 'A' || ch > 'Z');
if (ch == 'U') opt[i][j] = 0;
if (ch == 'D') opt[i][j] = 1;
if (ch == 'L') opt[i][j] = 2;
if (ch == 'R') opt[i][j] = 3;
}
s = 0; t = cnt << 1 | 1;
for (R int i = 1; i <= n; ++i)
for (R int j = 1; j <= m; ++j)
{
link(s, id[i][j], 1, 0);
link(id[i][j] + cnt, t, 1, 0);
for (R int k = 0; k < 4; ++k)
{
R int nx = i + dx[k], ny = j + dy[k];
link(id[i][j], id[cmod(nx, n)][cmod(ny, m)] + cnt, 1, k == opt[i][j] ? 0 : 1);
}
}
while (spfa()) mcmf();
printf("%d\n",ans );
return 0;
}
【bzoj3171】[Tjoi2013]循环格的更多相关文章
- BZOJ3171 Tjoi2013 循环格
传送门 Description 一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子.每个元素有一个坐标(行,列),其中左上角元素坐标为(0,0).给定一个起始位置(r,c) ,你可以沿着箭头 ...
- bzoj3171: [Tjoi2013]循环格(费用流)
传送门 其实这题的建图并不难(虽然我并没有想出来) 首先,每一个点的入度和出度必须为$1$ 那么我们考虑拆点 每个点的出度点向它能到达的点的入度点连边,容量$1$,如果方向为原来的方向则费用$0$否则 ...
- Bzoj 3171: [Tjoi2013]循环格 费用流
3171: [Tjoi2013]循环格 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 741 Solved: 463[Submit][Status][ ...
- [Tjoi2013]循环格
[Tjoi2013]循环格 2014年3月18日1,7500 Description Input 第一行两个整数R,C.表示行和列,接下来R行,每行C个字符LRUD,表示左右上下. Output 一个 ...
- 洛谷 P3965 [TJOI2013]循环格 解题报告
P3965 [TJOI2013]循环格 题目背景 一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子. 每个元素有一个坐标(行,列),其中左上角元素坐标为\((0,0)\).给定一个起始位\ ...
- BZOJ_3171_[Tjoi2013]循环格_最小费用最大流
BZOJ_3171_[Tjoi2013]循环格_最小费用最大流 Description 一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子.每个元素有一个坐标(行,列),其中左上角元素坐标为 ...
- [TJOI2013]循环格 费用流 BZOJ3171
题目背景 一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子.每个元素有一个坐标(行,列),其中左上角元素坐标为(0,0).给定一个起始位(r,c),你可以沿着箭头方向在格子间行走.即:如果 ...
- 【BZOJ3171】[TJOI2013] 循环格(网络流)
点此看题面 大致题意: 给你一个循环格,每个格子有一个方向.问你至少修改多少格子,才能使从每个格子出发都能回到原格子. 建图 这是道网络流题目,主要就是考虑如何建图. 我们可以把每个点拆成两个点,一个 ...
- bzoj 3171: [Tjoi2013]循环格
#include<cstdio> #include<iostream> #include<cstring> #define M 10000 #define inf ...
随机推荐
- 【Qt开发】设置Qt应用程序图标
[Qt开发]设置Qt应用程序图标 标签:[Qt开发] 首先,准备一个图标,例如:zx.ico,并新建一个文本文档,在里面添加一行: IDI_ICON1 ICON DISCARDABLE"zx ...
- C盘无损扩容(傻逼拯救者128G固态分两个盘)
下载DiskGenius.exe 进行拆分分区(我从d盘拆分出20G给c盘) 然后右键此电脑,管理->磁盘管理 选中刚分出来的20G空间指向到c盘
- dbvisualizer安装
1. 下载DbVisualizer安装包. 2.解压. 无论是哪个版本的dbvisualizer破解版, 都可以找到安装程序(例dbvis_windows-x64_10_0_10.exe), dbvi ...
- linux服务器上安装mysql
mysql版本:mysql-5.6.44-linux-glibc2.12-x86_64.tar linux操作系统和版本信息: 1.检查linux服务器上是否已安全mysql [root@localh ...
- D-多连块拼图
多连块是指由多个等大正方形边与边连接而成的平面连通图形. – 维基百科 给一个大多连块和小多连块,你的任务是判断大多连块是否可以由两个这样的小多连块拼成.小多连块只能平移,不能旋转或者翻转.两个小多连 ...
- 在Visual studio 2017中使用EF6连接MySQL
在Visual studio 2017中使用EF6连接Mysql ADO.NET Entity Framework 是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R Mapping) ...
- k3 cloud中单据体中文本自适应
在单据体中添加多行文本,然后设置本地配置,只读单元格自动换行
- 无锁版以时间为GUID的方法
之前的博客 将时间作为GUID的方法 中,我使用了锁.我在实际的使用中,错将锁的释放放在了if语句中,这纯粹是我的失误,导致了很严重的错误.因此我在想是否有无锁的将时间作为GUID的方式,答案是使用I ...
- 16.Linux-CentOS系统进入单用户模式修改root用户密码操作
问题描述: root用户密码忘记,进入单用户重置root用户密码 解决步骤: 1.重启服务器,在系统显示内核版本界面后“按E键”,进入内核启动项2.找到Linux16这一行段,将“ro”修改成“rw” ...
- Java并发(具体实例)——几个例子
一步步优化页面渲染功能 本节将模拟一个简单的页面渲染功能,它的作用是将HTML页面绘 ...