题目描述

Mirko在一个山镇找到了一份邮递员的工作。这个镇可以看作一个N*N的矩形。每个区域可能是以下之一:房子K,邮政局P,草地 ‘.’。每个区域都有一个海拔。

每天早上,Mirko要送信给镇上所有的家庭。他从邮局P处开始,可以向8个方向到相邻的一个区域,当他送完最后一份信后,他必须回到邮局。

现在用Mirko走过的路线中海拔最高点和最低点之差来表示他的疲劳程度。帮他计算出送出所有的信最少的疲劳值。

输入

第一行包含整数N(2<=N<=50)。接下来的N行表示矩形。P只出现一次,而K最少出现一次。

接下来N行每行包含N个正整数,表示相应区域的海拔。均小于1000000.

输出

单独的一行,一个整数,表示最小的疲劳值。

样例输入

2

P.

.K

2 1

3 2

样例输出

0

多么有(wei)趣(suo)的一道题啊!!!!!!!!!

多么有(wei)趣(suo)的一道题啊!!!!!!!!!

多么有(wei)趣(suo)的一道题啊!!!!!!!!!

(重要的事情说三遍)

题解很多人都yy到了——二分疲劳度,枚举最小海拔,搜索验证

但给我的结果是

TLE 2000ms+

超时代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#include<algorithm>
using namespace std;
struct node{
int x,y;
}p[2501];
int height[51][51];
int map[51][51];
int n,cnt;
set<int>h;
int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
void getmap()
{
int i,j;
char c[51];
for(i=1;i<=n;i++)
{
scanf("%s",c+1);
for(j=1;j<=n;j++)
if(c[j]!='.')p[++cnt].x=i,p[cnt].y=j;
}
}
int u[8]={-1,-1,-1,0,0,1,1,1},z[8]={-1,0,1,-1,1,-1,0,1};
void bfs()
{
queue<int>Q[2];
Q[0].push(p[1].x),Q[1].push(p[1].y);
if(!map[p[1].x][p[1].y])map[p[1].x][p[1].y]=1;
while(!Q[0].empty())
{
int x=Q[0].front(),y=Q[1].front();Q[0].pop(),Q[1].pop();
for(int i=0;i<8;i++)
if(x+u[i]>0&&x+u[i]<=n&&y+z[i]>0&&y+z[i]<=n&&!map[x+u[i]][y+z[i]])
{
map[x+u[i]][y+z[i]]=1;
Q[0].push(x+u[i]),Q[1].push(y+z[i]);
}
}
}
bool work(int minh,int maxh)
{
int i,j;
memset(map,-1,sizeof map);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(height[i][j]>=minh&&height[i][j]<=maxh)
map[i][j]=0;
bfs();
for(i=1;i<=cnt;i++)if(map[p[i].x][p[i].y]!=1)return 0;
return 1;
}
int main()
{
int i,j;
n=getint();
getmap();
set<int>::iterator iter;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
h.insert(height[i][j]=getint());
int l=0,r=1000000;
while(l<r)
{
bool p=0;
int mid=(l+r)>>1;
for(iter=h.begin();iter!=h.end();iter++)
if(work(*iter,*iter+mid)){p=1;break;}
if(p)r=mid;
else l=mid+1;
}
printf("%d",l);
}

orz=orz=orz=orz

过不了,正解也yy不到,那就只有拿起菜刀,剪枝去

拿着菜刀,细看每一段代码,刀尖落在了这一段:

for(iter=h.begin();iter!=h.end();iter++)
if(work(*iter,*iter+mid)){p=1;break;}

在枚举的过程中,总会枚举到一些最小海拔,大于了邮局或住宅的海拔,

或最大海拔,小于了邮局或住宅的海拔

岂能容忍?剪!!

设minh与maxh为停留点的最小最大海拔

更改为:

for(iter=h.begin();iter!=h.end();iter++)
if(*iter<=minh&&*iter+mid>=maxh)
if(work(*iter,*iter+mid))
{p=1;break;}

交上去 TLE 1108ms

剪了不少!

又挑到了一段:

int l=0,r=1000000;

二分范围能不能剪呢?

我猛然发现,最小疲劳一定不小于maxh-minh,而最大疲劳一定小于等于整张地图的最高海拔减最低海拔

改了之后交上去 TLE 1009ms

加油!努力!还差9ms

挑来挑去,还是在验证中的BFS中入手吧

我验证的方法是将不在海拔范围内的点标记后,从一号停留点开始遍历全图,看看每一个停留点是否被遍历到

等等,既然是遍历,没有其他最短路或极值的操作,那DFS的访问次数与BFS是一样的,而且BFS的系数还比较大

欣欣然将BFS改为DFS

怀着鸡冻的心情,我交了代码

AC 877ms

23333333333333333333333333333333333333333~~~~~~~~~~~~~~~~

功夫不负有心人,在精(sang)雕(xin)细(bing)琢(kuang)的剪枝下,AC了!!!!!

代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#include<algorithm>
using namespace std;
struct node{
int x,y;
}p[2501];
int height[51][51];
int map[51][51];
int n,cnt;
set<int>h;
int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
void getmap()
{
int i,j;
char c[51];
for(i=1;i<=n;i++)
{
scanf("%s",c+1);
for(j=1;j<=n;j++)
if(c[j]!='.')p[++cnt].x=i,p[cnt].y=j;
}
}
int u[8]={-1,-1,-1,0,0,1,1,1},z[8]={-1,0,1,-1,1,-1,0,1};
void bfs()
{
queue<int>Q[2];
Q[0].push(p[1].x),Q[1].push(p[1].y);
if(!map[p[1].x][p[1].y])map[p[1].x][p[1].y]=1;
while(!Q[0].empty())
{
int x=Q[0].front(),y=Q[1].front();Q[0].pop(),Q[1].pop();
for(int i=0;i<8;i++)
if(x+u[i]>0&&x+u[i]<=n&&y+z[i]>0&&y+z[i]<=n&&!map[x+u[i]][y+z[i]])
{
map[x+u[i]][y+z[i]]=1;
Q[0].push(x+u[i]),Q[1].push(y+z[i]);
}
}
}
void dfs(int x,int y)
{
int i;
for(i=0;i<8;i++)
if(!map[x+u[i]][y+z[i]]&&x+u[i]>0&&x+u[i]<=n&&y+z[i]>0&&y+z[i]<=n)
{
map[x+u[i]][y+z[i]]=1;
dfs(x+u[i],y+z[i]);
}
}
bool work(int minh,int maxh)
{
int i,j;
memset(map,-1,sizeof map);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(height[i][j]>=minh&&height[i][j]<=maxh)
map[i][j]=0;
//bfs();
dfs(p[1].x,p[1].y);
for(i=1;i<=cnt;i++)if(map[p[i].x][p[i].y]!=1)return 0;
return 1;
}
int main()
{
int i,j,minh=1<<30,maxh=0,k=1,_minh=1<<30,_maxh=0;
n=getint();
getmap();
set<int>::iterator iter;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
h.insert(height[i][j]=getint());
if(p[k].x==i&&p[k].y==j)
{
k++;
minh=min(minh,height[i][j]);
maxh=max(maxh,height[i][j]);
}
_minh=min(_minh,height[i][j]);
_maxh=max(_maxh,height[i][j]);
}
int l=maxh-minh,r=_maxh-_minh;
while(l<r)
{
bool p=0;
int mid=(l+r)>>1;
for(iter=h.begin();iter!=h.end();iter++)
if(*iter<=minh&&*iter+mid>=maxh)
if(work(*iter,*iter+mid))
{p=1;break;}
if(p)r=mid;
else l=mid+1;
}
printf("%d",l);
}

CQBZOJ 邮递员(直播剪枝技巧)的更多相关文章

  1. 深搜的剪枝技巧(三)——Sticks(可行性剪枝、上下界剪枝、最优性剪枝)

    小木棍(最优性剪枝.可行性剪枝) 一.问题描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,已知每段的长都不超过 50 .现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍 ...

  2. 一本通例题埃及分数—题解&&深搜的剪枝技巧总结

    一.简述: 众所周知,深搜(深度优先搜索)的时间复杂度在不加任何优化的情况下是非常慢的,一般都是指数级别的时间复杂度,在题目严格的时间限制下难以通过.所以大多数搜索算法都需要优化.形象地看,搜索的优化 ...

  3. hdoj1010 奇偶剪枝+DFS

    Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Othe ...

  4. POJ 2531 深搜剪枝

    题意:全局最大割. 分析:有相应的算法,数据量很小,可以枚举源点,汇点,最大流. 这里用DFS,状态定义:分成两个集合,刚开始S集合全部点,然后一个一个放,这是一个回溯的过程. 没剪枝也过了. 剪枝技 ...

  5. 剪枝的定义&&hdu1010

    半年前在POJ上遇到过一次剪枝的题目,那时觉得剪枝好神秘...今天在网上查了半天资料,终于还是摸索到了一点知识,但是相关资料并不多,在我看来,剪枝是技巧,而不是方法,也就是说,可能一点实用的小技巧,让 ...

  6. poj1011(DFS+剪枝)

    题目链接:https://vjudge.net/problem/POJ-1011 题意:给定n(<=64)条木棍的长度(<=50),将这些木棍刚好拼成长度一样的若干条木棍,求拼出的可能的最 ...

  7. poj1011 Sticks (dfs剪枝)

    [题目描述] George took sticks of the same length and cut them randomly until all parts became at most 50 ...

  8. LeetCode37 使用回溯算法实现解数独,详解剪枝优化

    本文始发于个人公众号:TechFlow,原创不易,求个关注 数独是一个老少咸宜的益智游戏,一直有很多拥趸.但是有没有想过,数独游戏是怎么创造出来的呢?当然我们可以每一关都人工设置,但是显然这工作量非常 ...

  9. noip2004提高组题解

    这次有两道题以前已经做过了,所以分数什么的也没有意义了.发现这年的难度设置极不靠谱,前三题都比较简单,最后一题太难,不知道出题人怎么想的. 第一题:储蓄计划 模拟. 第二题:合并果子 贪心.每次选最小 ...

随机推荐

  1. 【12.78%】【codeforces 677D】Vanya and Treasure

    time limit per test1.5 seconds memory limit per test256 megabytes inputstandard input outputstandard ...

  2. 使用Squid做代理服务器,Squid单网卡透明代理配置详解(转)

    使用Squid做代理服务器 说到代理服务器,我们最先想到的可能是一些专门的代理服务器网站,某些情况下,通过它们能加快访问互联网的速度.其实,在需要访问外部的局域网中,我们自己就能设置代理,把访问次数较 ...

  3. C语言中的断言

    一.原型定义:void assert( int expression ); assert宏的原型定义在<assert.h>中,其作用是先计算表达式 expression ,如果expres ...

  4. javascript继承的几种方法

    继承是面向对象编程中很重要的概念,在其它面向对象的语言中大都很简单,例如java中有关键词extends来实现 javascript语言在ES6也新增了extends关键词可以实现继承,用法与java ...

  5. unity3D 自定义公告牌

    前言 有时候我们希望公告牌跟随镜头旋转永远平行面向屏幕,同时跟随镜头缩放缩放大小不变(镜头远离物体,正常物体视觉效果变小,但公告牌视觉大小比例不变),或者跟随镜头缩放变化,本文记录C#脚本的两种实现方 ...

  6. webapp开发之IIS进程调试

    1.背景 1.当我的手机连接电脑的时候想要调试居然连接不上,之后我将项目发布之后才可以请求(同一局域网下) 2.你们不觉得发布到IIS再附加进程太烦了么?看了看网上全是这种方法,这不科学!VS已经提供 ...

  7. [梁山好汉说IT] 如何理解脑裂问题

    [梁山好汉说IT] 如何理解脑裂问题 这个系列是通过梁山好汉的例子来阐述一些IT概念. 1. 问题描述 Split-brain, 就是在集群环境中,因为异常情况发生后,产生两个子集群.每个子集群都选出 ...

  8. ELK学习实验012:Logstash的安装和使用

    一 logstash安装 1.1下载包 [root@node1 ~]# cd /usr/local/src/ [root@node1 src]# wget https://artifacts.elas ...

  9. linux大盘格式化分区

    Linux 实例的磁盘管理 对于 Linux 系统上的大磁盘,也要采用 GPT 分区格式, 也可以不分区, 把磁盘当成一个整体设备使用. 在 Linux 上一般采用 XFS 或者 EXT4 来做大盘的 ...

  10. js中this指向问题(call,apply,bind)

    call.apply.bind的作用是改变函数运行时this的指向. 如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下 ...