@description@

给出一个N*M的网格图,有一些方格里面存在城市,其中首都位于网格图的左上角。

你可以沿着网络的边界走,要求你走的路线是一个环并且所有城市都要被你走出来的环圈起来,即想从方格图的外面走到任意一个城市一定要和你走的路线相交。

你沿着方格的边界走是需要费用的,不同的边界费用可能不同,求最小代价。

1<=N,M<=400,走过边界的代价为正整数且不超过10^9

Sample Input

3 3

1 0 1

0 0 0

0 1 0

2 1 1 3

5 6 1 1

2 1 1 3

2 1 1

3 4 1

4 1 1

5 1 2

Sample Output

22

HINT

@solution@

人类智慧题。

首先路径可能重复经过边或者点这件事很难描述,而且本身很不自然。

我们将走过一条边看成两种,一种从左/上侧经过,一种从右/下侧经过(竖着的边就是左右侧,横着的边就是上下侧)。

比如下图就是转变后的例子:

这样就不会经过重边重点,而且可以很自然地描述包含一条边/一个格点的概念。

假如没有额外限制,就只是找一个简单最小环(并且一定包含左上角,因为左上角一定有一个城市)。

如果左上角的格点为 (0, 0),那么跑一个从 (0, 1) 到 (1, 0) 的最短路即可。

但是我怎么表达包含一个城市的概念。

假如只有左上角有城市,则只要确定起点为 (0, 1) 终点为 (1, 0) 就可以包含左上角的城市了。

假如城市与城市之间有些割不断的联系,那么我跑最短路时不横断这些联系,既然我已经包含了左上角,就可以保证所有城市都在最小环里面了。

有一个结论:左上角的格点到城市的左上角的最短路一定会被包含其中

直观地理解:

假如蓝色是一种方案,绿色是最短路。当我把黄色区域加入进去过后,因为是最短路,所以方案一定不会变差(否则绿色就不是最短路了)。

即我们总可以用最短路代替边界。

那么建出最短路树,只要不横断这些树边就一定合法了。

具体实现,我们将一个格点拆成四个,建出一个类似于如下的图:

拆出来的四个点之间,如果不会横断最短路就连边权为 0 的边。

同时需要注意,一个城市四个角对应的点不向其他点连边。

左上角的格点拆出来的对应左上角的那个点不连边,跑左上角的格点对应的右上角到左上角的格点对应左下角的那个点的最短路。

两个相邻格点之间也要连,边权即原图中边的边权。

@accepted code@

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
#define rep(G, x) for(Graph::edge *p = G.adj[x];p;p = p->nxt)
typedef long long ll;
const int MAXN = 400 + 5;
const int MAXV = 4*MAXN*MAXN;
const int MAXE = 16*MAXV + 5;
const ll INF = (1LL<<60);
struct Graph{
struct edge{
int to, dis;
edge *nxt;
}edges[MAXE + 5], *adj[MAXV + 5], *ecnt;
Graph() {ecnt = edges;}
void addedge(int u, int v, int w) {
edge *p = (++ecnt);
p->to = v, p->dis = w, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->dis = w, p->nxt = adj[v], adj[v] = p;
// printf("! %d %d %d\n", u, v, w);
}
void clear(int n) {
ecnt = edges;
for(int i=1;i<=n;i++)
adj[i] = NULL;
}
}G;
int hp[MAXV + 5], pre[MAXV + 5];
ll f[MAXV + 5], dis[MAXV + 5];
void update(int x, ll k, const int &n) {
f[x] = k;
while( x ) {
hp[x] = x;
if( (x<<1) <= n && f[hp[x<<1]] < f[hp[x]] )
hp[x] = hp[x<<1];
if( (x<<1|1) <= n && f[hp[x<<1|1]] < f[hp[x]] )
hp[x] = hp[x<<1|1];
x >>= 1;
}
}
void dijkstra(int s, const int &n) {
for(int i=1;i<=n;i++)
dis[i] = f[i] = INF, hp[i] = i;
update(s, dis[s] = 0, n);
while( f[hp[1]] != INF ) {
int x = hp[1]; update(x, INF, n);
rep(G, x) {
if( dis[x] + p->dis < dis[p->to] ) {
update(p->to, dis[p->to] = dis[x] + p->dis, n);
pre[p->to] = x;
}
}
}
}
int id[MAXN + 5][MAXN + 5], cnt;
int A[MAXN + 5][MAXN + 5], B[MAXN + 5][MAXN + 5], C[MAXN + 5][MAXN + 5];
bool tg[MAXV + 5];
queue<int>que;
int ind(int x, int p) {
return (x - 1) * 4 + p;
}
bool check(int x, int y) {
return (pre[x] == y && tg[x]) || (pre[y] == x && tg[y]);
}
int main() {
int N, M; scanf("%d%d", &N, &M);
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
scanf("%d", &A[i][j]);
for(int i=1;i<=N+1;i++)
for(int j=1;j<=M+1;j++)
id[i][j] = (++cnt);
for(int i=1;i<=N;i++)
for(int j=1;j<=M+1;j++) {
scanf("%d", &B[i][j]);
G.addedge(id[i][j], id[i+1][j], B[i][j]);
}
for(int i=1;i<=N+1;i++)
for(int j=1;j<=M;j++) {
scanf("%d", &C[i][j]);
G.addedge(id[i][j], id[i][j+1], C[i][j]);
}
dijkstra(1, cnt);
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
if( A[i][j] ) que.push(id[i][j]), tg[id[i][j]] = true;
while( !que.empty() ) {
int f = que.front(); que.pop();
if( !tg[pre[f]] )
que.push(pre[f]), tg[pre[f]] = true;
}
G.clear(cnt);
for(int i=1;i<=N;i++)
for(int j=1;j<=M+1;j++) {
if( !A[i][j-1] )
G.addedge(ind(id[i][j], 3), ind(id[i+1][j], 2), B[i][j]);
if( !A[i][j] )
G.addedge(ind(id[i][j], 4), ind(id[i+1][j], 1), B[i][j]);
}
for(int i=1;i<=N+1;i++)
for(int j=1;j<=M;j++) {
if( !A[i-1][j] )
G.addedge(ind(id[i][j], 1), ind(id[i][j+1], 2), C[i][j]);
if( !A[i][j] )
G.addedge(ind(id[i][j], 4), ind(id[i][j+1], 3), C[i][j]);
}
for(int i=1;i<=N+1;i++)
for(int j=1;j<=M+1;j++) {
if( i == 1 && j == 1 ) continue;
if( !(check(id[i][j], id[i-1][j]) || A[i-1][j] || A[i-1][j-1]) )
G.addedge(ind(id[i][j], 1), ind(id[i][j], 2), 0);
if( !(check(id[i][j], id[i][j-1]) || A[i][j-1] || A[i-1][j-1]) )
G.addedge(ind(id[i][j], 2), ind(id[i][j], 3), 0);
if( !(check(id[i][j], id[i+1][j]) || A[i][j-1] || A[i][j]) )
G.addedge(ind(id[i][j], 3), ind(id[i][j], 4), 0);
if( !(check(id[i][j], id[i][j+1]) || A[i-1][j] || A[i][j]) )
G.addedge(ind(id[i][j], 4), ind(id[i][j], 1), 0);
}
dijkstra(ind(1, 1), ind(cnt, 4));
printf("%lld\n", dis[ind(1, 3)]);
}

@details@

只用了 N 没有用 M 这个变量的人一定不止我一个人吧。

所以 2014 年出的题我现在还是不会。

@bzoj - 4356@ Ceoi2014 Wall的更多相关文章

  1. BZOJ4356 : Ceoi2014 Wall

    求出左上角到每个需要保护的点左上角的最短路树,那么最优解一定圈住了它们. 然后将每个点拆成四个点,四个点之间如果没跨越最短路树的树边,那就连0权边. 每个需要保护的点四周4个点都不可通行,求出最短路即 ...

  2. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  3. BZOJ 1665: [Usaco2006 Open]The Climbing Wall 攀岩

    题目 1665: [Usaco2006 Open]The Climbing Wall 攀岩 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 197  Sol ...

  4. 【BZOJ】1665: [Usaco2006 Open]The Climbing Wall 攀岩(spfa)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1665 这题只要注意到“所有的落脚点至少相距300”就可以大胆的暴力了. 对于每个点,我们枚举比他的x ...

  5. BZOJ 1113 Wall ——计算几何

    凸包第一题. 自己认为自己写的是Andrew 其实就是xjb写出来居然过掉了测试. 刚开始把pi定义成了int,调了半天 #include <map> #include <cmath ...

  6. 【BZOJ】【2120】数颜色 & 【2453】维护队列

    莫队算法 分块大法吼 这题乍一看跟HH的项链很像啊……只是多了一个修改操作……然而我就不会做了

  7. [Usaco2006 Open]The Climbing Wall 攀岩

    Description One of the most popular attractions at the county fair is the climbing wall. Bessie want ...

  8. BZOJ 2127: happiness [最小割]

    2127: happiness Time Limit: 51 Sec  Memory Limit: 259 MBSubmit: 1815  Solved: 878[Submit][Status][Di ...

  9. [poj1113][Wall] (水平序+graham算法 求凸包)

    Description Once upon a time there was a greedy King who ordered his chief Architect to build a wall ...

随机推荐

  1. mybatis深入理解(一)-----Mybatis初始化机制详解

    对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章将通过以下几点详细介绍MyBatis的初始化过程. 一. MyBatis的初始化做了什么 1.configuration ...

  2. 关于python的列表操作(二):排序,统计

    # 列表操作 num_list = [2, 5, 8, 6, 7, 9, 5, 7] # 升序 num_list.sort() print(num_list) # 降序 num_list.sort(r ...

  3. docker-4-Dockerfile配置文件详解

    ​ Dockerfile简单一点就是描述你这个镜像安装了哪些软件包,有哪些操作,创建了什么东西.有些人喜欢用 docker commit 命令去打包镜像,这样是不好的,首先commit出来的镜像比你使 ...

  4. js封装ajax的方法

    常用的ajax请求方法封装 /** * ajax请求的封装代码 */ function ajaxPost(url, params, cb) { $.ajax({ type : 'post', url ...

  5. python 随机模块random

  6. NOIP模拟 17.9.28

    公交车[问题描述]市内有

  7. Laravel 批量替换某个字段

    Likeword::offset(16854)->chunk(100, function ($word_list) { foreach ($word_list as $word) { $new ...

  8. Codeforces Round #410 (Div. 2) A. Mike and palindrome【判断能否只修改一个字符使其变成回文串】

    A. Mike and palindrome time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  9. Leetcode62.Unique Paths不同路径

    一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为"Start" ). 机器人每次只能向下或者向右移动一步.机器人试图达到网格的右下角(在下图中标记为" ...

  10. VirtualBox安装,VirtualBox安装CentOS

    1.进入VirtualBox官网下载页,找到对应的版本 https://www.virtualbox.org/wiki/Downloads 按步骤安装好 2.进入CentOS官网下载页,找到对应的版本 ...