【COCI】邮递员 2271
题目描述
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
分析: 每个点都有一个海拔,而要我们求符合要求的路线中,海拔值最高与最低的值的差最小。根据经验,大部分求最大-最小的题都是通过二分差值来AC的。于是马上得出一个复杂度略(很)高的解法:
1. 二分最大-最小
2. 枚举最小,算出最大
3. dfs||bfs, 验证算出的范围是否能让mirko完成任务(将范围外的点删去)
4. 输出答案
估算时间复杂度:
(log 1e6) //二分
* 1e6 //枚举下界
* 5050//dfs||bfs
有点大
优化一: 海拔值只有2500种,意味着我们只需要枚举下界2500次,即离散化。有多种方式,但为了简单并且方便,我推荐用STL的set||sort,高效又好写。
复杂度变为:20(log 1e6) 2500 *2500 = 125000000(明显超时)
优化二:由于房子你必须经过,所以枚举下界和上界时下界小于等于房子的最小值,上界大于等于房子的最大值;(最强力的剪枝,直接快了1s左右)
优化三:其实这个有点卡数据,dfs和bfs使用时枚举方向的数组顺序要找好,大家可以试试不同的顺序,也许就AC了呢,楼主就靠这个把时间从1000打到了985,好不容易AC(见代码)。
优化四:dfs时判断点能不能走,用vis,在函数外预处理,反正楼主靠此搞了300ms回来。
贴AC代码:985ms
#include<cstdio>
#include<cstring>
#include<set>
#include<queue>
#include<algorithm>
#define maxn 55
#define F first
#define S second
using namespace std;
set<int>s;
int n,dis[maxn][maxn],cnt_k;
char map[maxn][maxn];
pair<int,int>b,now,k[2505];
int U[8]={-1,-1,-1,0,0,1,1,1},z[8]={-1,0,1,-1,1,-1,0,1};
bool vis[maxn][maxn];
bool check(int u,int v){
return u>0&&u<=n&&v>0&&v<=n&& !vis[u][v];
}
void get(int &res)
{
res=0;
int flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')res=res*10+c-48,c=getchar();
res*=flag;
}
void dfs(int x,int y){
int u,v;
for(int i=0;i<8;i++)
if(check(u=x+U[i],v=y+z[i]))
{
vis[u][v]=1;
dfs(u,v);
}
}
bool search(int head,int tail){
memset(vis,-1,sizeof vis);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(dis[i][j]>=head&&dis[i][j]<=tail)
vis[i][j]=0;
dfs(b.F,b.S);
for(int i=0;i<cnt_k;i++)
if(vis[k[i].F][k[i].S]!=1)
return 0;
return 1;
}
int main(){
int maxh,minh,_minh=2e9,_maxh=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",map[i]+1);
for(int j=1;j<=n;j++){
if(map[i][j]=='P')
b=make_pair(i,j);
if(map[i][j]=='K')
k[cnt_k++]=make_pair(i,j);
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
get(dis[i][j]);
s.insert(dis[i][j]);
}
_minh=min(_minh,dis[b.F][b.S]);
_maxh=max(_maxh,dis[b.F][b.S]);
for(int i=0;i<cnt_k;i++){
_minh=min(_minh,dis[k[i].F][k[i].S]);
_maxh=max(_maxh,dis[k[i].F][k[i].S]);
}
set<int>::iterator it=s.end();
it--;
maxh=*it;
minh=*s.begin();
int l=_maxh-_minh,r=*(it)-*(s.begin()),mid;
while(l<r){
bool flag=0;
mid=(l+r)>>1;
for(it=s.begin();it!=s.end();it++)
{
if(*it>_minh || *it+mid<_maxh) continue;
if(search(*it,*it+mid))
{
flag=1;
break;
}
}
if(!flag) l=mid+1;
else r=mid;
}
printf("%d",l);
}
UPD:几年后又码一遍:
#include<bits/stdc++.h>
using namespace std;
int n,sx,sy;
ch