您的位置:首页 > 其它

8.15-30题目归档

2017-08-21 16:24 99 查看
21-题3 圣诞岛的宝藏

【问题描述】

Angel走进一间门虚掩的房间,房间里竟然满是宝藏!,房间被分成N*N的格子, N <= 1000。Angel站在(1,1)的位置上。不过,Angel只能向下或向右走,再回到(1,1)点。问最少取多少次,能够把所有的宝藏都取完?

【输入格式】

第一行N。

以后N行,每行N个字符,“.”代表空格子,“*”代表宝藏。

【输出格式】

一行,代表至少要取多少次,才能把宝藏取完。

【输入样例】

3



.*.

*..

【输出样例】

2

可以发现:对于一个点有宝藏的点x,y,其右上角都是不能取得。因为如果能取得了其右上角[x+1,y-n]的宝藏,而Angel又只能向右或向下走,必然不可能我们再走到[x,y]。所以可以将x从小到大排序,然后在y上做最长上升序列。因为如果上升的话就取不到n,而这些点不能一起取,而剩下的都可以在这些路径基础上走出来。

如果用O(N^2)的LIS可能会超时(极端情况下1000*1000全都是宝藏)。所以采用树状数组+LIS或者二分+LIS。时间复杂度为O(nlogn)

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int N=1100;
char sr

;
int k,num
;
struct cc
{
int x,y;
}a[N*N];
inline bool cmp(const cc &a,const cc &b)
{
return a.x>b.x||a.x==b.x&&a.y>b.y;
}
int top,pos;
int main()
{
freopen("treasure.in","r",stdin);
freopen("treasure.out","w",stdout);
n=read();
for(int i=1;i<=n;++i)
{
scanf("%s",sr[i]+1);
for(int j=1;j<=n;++j)
if(sr[i][j]=='*')
{
++top;
a[top].x=i;
a[top].y=j;
}
}
sort(a+1,a+1+top,cmp);

for(int i=1;i<=top;++i)
{
pos=lower_bound(num+1,num+k+1,a[i].y)-num;//num是数组指针
if(pos>k) ++k;
num[pos]=a[i].y;
}
//num即为最长上升序列
printf("%d",k);
}


矩形

21-题2

第2题 矩形

【问题描述】

在一个空间坐标系里,摆放了许许多多矩形,而对于每一个矩形,它的每条边都与某一条坐标轴平行。

有些矩形之间是相互接触的,而有些不是。给出两个矩形,如果他们拥有任何公共的部分,那么它们就被认为是相互接触。

现在给出一系列矩形,要你求,有多少对矩形相互接触。

【输入格式】

第一行包含一个整数N(1≤N≤2000),表示矩形数量。

接下来的N行,每行包含6个整数。前三个数是矩形某个顶点的坐标值,后三个则是其对角的坐标值。由于所有边都平行于坐标轴,我们总能确定下这个矩形每个顶点的位置。

所有的坐标值都是正整数,且不超过1000。

【输出格式】
输出一个整数,表示有多少对矩形相互接触。

【测试样例】

prostor.in
3
1 1 1 1 3 3
1 3 3 1 6 6
1 4 4 1 5 5

prostor.out
2

prostor.in
3
15 10 10 15 20 20
10 15 10 20 15 20
10 10 15 20 20 15

prostor.out
3

prostor.in
5
4 4 5 4 3 2
5 3 2 4 3 1
5 4 3 1 1 3
1 4 3 1 5 4
5 5 4 5 4 2
prostor.out
4


首先联想一下二维时候的情景。二维的时候,如何判断两个矩形是否重叠呢?可以通过判断相应角大小来实现。比如:



拓展到三维的角度也应该是如此:

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int N=2100;
int a
[7];
inline bool check(int x,int y)
{
for(int i=1;i<=3;++i)
if(a[x][i+3]<a[y][i]||a[y][i+3]<a[x][i]) return 0;
return 1;
}
int main()
{
freopen("prostor.in","r",stdin);
freopen("prostor.out","w",stdout);
n=read();
for(int i=1;i<=n;++i)
{
for(int j=1;j<=6;++j) a[i][j]=read();
for(int j=1;j<=3;++j)
if(a[i][j]>a[i][j+3]) swap(a[i][j],a[i][j+3]);//使a[i][j]<a[i][j+3]
}
int ans=0;
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
ans+=check(i,j);
cout<<ans;

}


最聪明的机器人

题2 最聪明的机器人(robotB)

【背景】

Wind设计了很多机器人。但是它们都认为自己是最强的,于是,一场比赛开始了~

【问题描述】

机器人们都想知道谁是最聪明的,于是它们进行如下一种游戏。

这个游戏由2次机器人进行,2个机器人分别报出一个数n1,n2,谁报得大,就以这个数作为基数,并由它先开始,轮流进行如下操作:

选取一个不大于基数的素数或者1,从基数中扣掉它。谁把基数扣到0,谁就赢了。

为了公平,他们会进行10次比赛,请你分别输出这10次谁获胜了。

【输入格式】robotB.in

每组测试数据均有10行

每行2个数n1,n2 (n1,n2<=maxlongint n1<>n2)

【输出格式】robotB.out

对每组测试数据输出10行,每行一个整数1或2

表示哪个机器人能获胜

【输入样例】

10 9

8 10

10 7

6 10

8 9

9 7

6 9

9 5

3 2

1 2

【输出样例】

1

2

1

2

2

1

2

1

1

2

【注释hint】

聪明的机器人当然会采取最优策略

1.任何一个大于4的数都可以被分成4k,4k+1,4k+2,4k+3的形式。对于4k+1,下一轮可以让其变为4k。4k+2,4k+3也同样可以变成4k。

2.任何一个大于2的质数都可以分解成4k+1或者4k-1的形式

3.如果先手者或者当前执行者的数是4的倍数,那么他的对手可以再将他变成4的倍数或者0。

所以:摸到4的倍数的会输,摸到非4的倍数则能赢。

定向越野

题4 定向越野(adven)
【问题描述】
交大闵行校区位于上海西南处,离市中心距离略远,因此占地面积巨大,足有5000亩之多,校园周长达8公里,因而校团委学生会充分利用了资源,每年都要在校园里举办定向越野比赛,但规则与普通定向越野不同,每个队被要求从某个起点出发最后到达终点,只要是地图上每个被标注的点都可以走,经过一个点时必须在打卡器上打卡作记录,记录该点的打卡器所在位置的海拔高度,高度用一个非负整数来量度,该数将会被所保存在卡中。最后到达终点时,该队的成绩就为卡中记录的最大数与最小数之差,差最小的队伍将摘取桂冠。
ZZ和他的同学也参与了这项运动,拿到地图后,他们想要迅速找到一条最佳路线以确保获得冠军。
PS:其实光脑子好能算出最佳路线还不够,还得能跑,但我们假设ZZ他们队个个都是SUPERMAN,只要你帮助他们找到了最佳路线,他们就能获得冠军。
【输入格式】
数据的第一行包含一个正整数n,表示校园地图上共有n*n个被标注的点(n≤100)
接下来n行每行有n个非负整数ai,j,表示该点的打卡器所在位置的高度。(ai,j≤200)
ZZ和他的同学从(1,1)出发,目的地为(n,n)
【输出格式】adven.in
文件包含一个整数,即最小的高度差的值
【输入样例】adven.out
5
1 1 3 6 8
1 2 2 5 5
4 4 0 3 3
8 0 2 2 4
4 3 0 3 1
【输出样例】
3
【样例说明】
最佳路线为(1,1)-- (1,2)-- (2,2)-- (2,3)-- (3,3)-- (4,3)-- (4,4)-- (5,4)-- (5,5)。路线上最高高度为3,最低高度为0,所以答案为3。当然,最佳路线可能不止一条。
【数据规模】
对于40%的数据, 保证N≤20
对于100%的数据,保证N≤100


全图的最低高度和最高高度是可以得到的。可以固定下最低点,通过二分法来枚举最高点,并通过搜索,判断最后能否走到[n,n]来确定该方案是否可行。

原题解:

初看此题,可能很多同学会想到使用动态规划的方法去求解。然而,随之而来的问题是

如何去表示状态。这是比较难解决的问题。

不妨换个思路。对整个地图,能否采用搜索的方法,去寻找一条道路,然后找出这条路

中每个点海拔的最高和最低值呢。纯粹的搜索显然会超时,转变一下想法。假如我们知道最

高和最低值,然后去判断从起点到终点是否存在这样的一条路,若存在则缩小这个最高最低

的差值,并重复这样的判断,理论上是可行的。

接下来的问题便是如何来确定这最高和最低值了。再读一下题目,每个点的海拔高度的

范围为:0-200。这样的话就可以尝试枚举最高最低点的高度,进而判断是否存在一条路径。

在枚举的同时也可以剪掉很多的比当前得到的最优解不优的高度点。在枚举最高最低点的基础上,我们可以采用这样的方法:枚举最低点,然后二分最高点,进行判断。这样的话可以将复杂度缩小一个常数,更优!

总结:

1. 要注意题中数据的范围,如本题高度的范围,可能会对解题有所帮助。

2. 当一个思路无法继续的时侯,不妨换个思想。尤其是不要将简单题目复杂化

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int N=110;
int a

;
struct cc
{
int x,y;
}q[N*N];
const int dx[]={1,-1,0,0};
const int dy[]={0,0,1,-1};
int Min;
bool vis

;
inline bool check(int Max)
{
if(a[1][1]>Max||a[1][1]<Min) return 0;
int t=0,w;
q[w=1].x=1;
q[w=1].y=1;
memset(vis,0,sizeof(vis));
vis[1][1]=1;
int e,ux,uy,vx,vy;
while(t<w)
{
++t;
ux=q[t].x;
uy=q[t].y;
for(e=0;e<=3;++e)
{
vx=ux+dx[e];
vy=uy+dy[e];
if(vx>=1&&vx<=n&&vx>=1&&vy<=n)
if(!vis[vx][vy]&&a[vx][vy]>=Min&&a[vx][vy]<=Max)
{
vis[vx][vy]=1;
++w;
q[w].x=vx;
q[w].y=vy;
}
}
}
return vis

;
}
int ans;
int main()
{
freopen("adven.in","r",stdin);
freopen("adven.out","w",stdout);
n=read();
int e=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
{
a[i][j]=read();
e=max(e,a[i][j]);
}
int l,r,mid;
ans=1e9;
for(Min=0;Min<=e;++Min)
{
l=Min;
r=e;
while(l+1<r)
{
mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid;
}
if(check(r)) ans=min(ans,r-Min);
if(check(l)) ans=min(ans,l-Min);
}
cout<<ans;
}


机器选择

8-22第4题 机器选择

【题目描述】

自从省队NOI赛前集训在scz举行之后,一个名叫cs1.6.exe的文件开始在机房广泛使用起来。每天大家都要找神犇小X借移动硬盘,考里面的这个文件。

由于机房里需要考这个文件的人太多了,每天都要花一段时间一个人一个人的去拷贝。小T觉得这实在是太麻烦了,就想找一个一劳永逸的方法。

小T调查了一下,机房有n台机器,且有局域网,所有机器通过一些网线连接起来,其整个布局是一个树形结构,即任意两台机器间都有且仅有一条路径。小T想在其中某一台机器上储存这个文件,需要的同学就可以直接通过局域网来下载这个文件。

网络上信息传输是需要时间的,我们定义两台机器间数据传输的时间为连接这两台机器的路径所包含的网线数量。虽然机房里通过局域网传个文件是很快的,但对于急不可耐的同学们来说,一分一秒都是宝贵的,文件传输越快越好。所以小T要选择一台机器存储文件,使得所有机器下载这个文件需要的总时间(即最后一台机器完成下载的时间)尽可能短。

现在,你需要给出这个最短时间,以便让小T看看他的决策是否最优。

【输入数据】

从selc.in中读入数据。

第1行:一个整数n。

第2~n行:两个整数u、v,即u、v两台机器间有一条网线连接。机器从1~n编号。

输入数据保证是一个连通的树型结构。

【输出数据】

向selc.out中输出数据。

1行一个整数,即最短的时间。

【数据范围】

对于30%的数据,n≤100;

对于50%的数据,n≤1000;

对于100%的数据,2≤n≤100000。

【输入输出样例】

selc.in selc.out

5

3 2

2 1

5 2

2 4 1

树上的最长链称为树的直径。

用两次dfs找出树的直径,然后除以2取整就行了。

棋盘

第2题 棋盘

【问题描述】

在一个N×N的棋盘上,一些格子里填了字母,并且在整个棋盘上,没有哪个字母出现两次或以上。在这些填了字母的格子中,有一些这样的情况:三个格子的中心处在一条直线上。现在你的任务是,找出所有这样三个一组的格子,满足它们在一直线上。输出有多少组即可。

【输入格式】

第一行包含一个整数N(3≤N≤100),表示棋盘尺寸。

下面N行,每行N个字符描述了整个棋盘。其中包只含大写字母,空格则用点表示。

【输出格式】

输出所有的三个一组的总数。

【测试样例】

trojke.in

4

…D

..C.

.B..

A…

trojke.out

4

trojke.in

5

..T..

A….

.FE.R

….X

S….

trojke.out

3

trojke.in

10

….AB….

..C….D..

.E……F.

…G..H…

I……..J

K……..L

…M..N…

.O……P.

..Q….R..

….ST….

trojke.out

0

这题关键是只有26个字母,因此可以枚举每一个字母的位置斜率来判断

时间复杂度为O(n^3)

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
struct cc
{
int x,y;
}a[27];
int main()
{
freopen("trojke.in","r",stdin);
freopen("trojke.out","w",stdout);
int n,tot=0;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%*");
for(int j=1;j<=n;++j)
{
char ch;
scanf("%c",&ch);
int num=int(ch)-'A'+1;
if(num>=1&&num<=26)
{
++tot;
a[tot].x=i;
a[tot].y=j;
}
}
}
int ans=0;
for(int i=1;i<=tot;++i)
for(int j=i+1;j<=tot;++j)
for(int k=j+1;k<=tot;++k)
if((a[i].y-a[k].y)*(a[i].x-a[j].x)==(a[i].y-a[j].y)*(a[i].x-a[k].x))++ans;//斜率相同..
cout<<ans;
}


第3题 架设电线

【问题描述】

在JS大学参加数学夏令营的同学对宿舍供电的稳定性给予了极大的关注,因为大家都住在7~16层,如果停电,呜呜~~~~

所以,他们要求负责人把那些老旧的电线换成性能更好的新电线。新的电线架设在已有的N 根电线杆上,第i根电线杆的高度为Hi米。电线总是从一根电线杆的顶端被引到相邻的那根的顶端,如果这两根电线杆的高度不同,那么负责人就必须为此支付“C * 电线杆高度差”的费用。当然,他不能移动电线杆,只能按原有的顺序在相邻杆间架设电线。

负责人认为,加高某些电线杆能减少架设电线的总花费,尽管这项工作也需要支出一定的费用。更准确地,如果他把一根电线杆加高X米的话,他得为此付出X^2的费用。

负责人找到了小Y,提出这个“无理”要求的队伍的首领(因为他最怕爬楼了,所以他也最积极),要求小Y帮忙计算一下如果合理地进行这两种工作,他最少要在这个电线改造工程上花多少钱。

不过,小Y不是有点懒而是相当懒,所以他把这个简单的任务交给了在座的各位。

【输入格式】

第1行:两个整数:N和C(2≤N≤100000,1≤C≤100),之间用一个空格隔开。

第2~N+1行:第i+1行仅有一个整数:Hi(1≤Hi≤100)。

【输出格式】

仅一1行一个整数,表示负责人完成电线改造工程所需要的最小花费。

【输入样例】

5 2

2

3

5

1

4

【输出样例】

15

【样例说明】

一共有5根电线杆,在杆间拉电线的费用是每米高度差2元。在改造之前,电线杆的高度依次为2,3,5,1,4米。

最好的改造方法是:负责人把第一根电线杆加高1米,把第四根加高2米,使得它们的高度依次为3,3,5,3,4米。这样花在加高电线杆上的钱是5元。此时,拉电线的费用为2*(0+2+2+1) = 10,总花费为15元。

状态:设f[i][j]表示前i根电线杆,高度为J时的最小花费。
转移方程:f[i][j] = min(f[i-1][k]+abs(k-j)*c+(j-a[i])^2) (k表示前面那根电线杆的高度)
提出所有与k无关的量:
f[i][j] = min(f[i-1][k]+abs(k-j)*c)+(j-a[i])^2
然后去掉绝对值:
设P = min(f[i-1][k]+abs(k-j)*c),Q = (j-a[i]^2)
f[i][j] = P + Q
设A = min(f[i-1][k]+k*c)-j*c
B = min(f[i-1][k]-k*c)+j*c
记C[X][i] = min(f[i-1][k]+k*c)
D[X][i] = min(f[i-1][k]-k*c)
则C[i-1][j]+j*c = A
D[i-1][j+1]-j*c = B
由于只需要用到i-1行的值,所以可以用滚动数组。
高度只能增高,所以j,k>a[i]
时间复杂度为O(N*100),空间复杂度为O(2*100*3)


#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m,c,ans,Max;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int inf=1e9;
const int N=100001;
int now,past,h
,f[2][102],C[2][102],D[2][102];
int Sqr(int x){return x*x;}
int main()
{
freopen("telewire.in","r",stdin);
//  freopen("telewire.out","w",stdout);
memset(f,63,sizeof(f));
memset(C,63,sizeof(C));
memset(D,63,sizeof(D));
Max=-inf;
n=read();
c=read();
for(int i=1;i<=n;++i)
h[i]=read(),Max=max(Max,h[i]);
f[1][h[1]]=0;
for(int i=h[1];i<=Max;++i)
{
f[1][i]=f[1][h[1]]+Sqr(i-h[1]);
C[1][i]=min(C[1][i-1],f[1][i]-c*i);
}
for(int i=Max;i;--i)
D[1][i]=min(D[1][i+1],f[1][i]+c*i);
now=0;past=1;
//获得第一根电线杆最小值

for(int i=2;i<=n;++i,swap(now,past))
//now,past即滚动数组
{
memset(f[now],63,sizeof(f[now]));
memset(C[now],63,sizeof(C[now]));
memset(D[now],63,sizeof(D[now]));
for(int j=h[i];j<=Max;++j)
{
f[now][j]=min(C[past][j]+c*j+Sqr(j-h[i]),D[past][j]-c*j+Sqr(j-h[i]));

C[now][j]=min(C[now][j-1],f[now][j]-c*j);
}
for(int j=Max;j;--j)
D[now][j]=min(D[now][j+1],f[now][j]+c*j);
}
ans=inf;
for(int i=h
;i<=Max;++i) ans=min(ans,f[past][i]);
cout<<ans;
}


题2 苹果旅游(travel)

【背景】

xiaoT发现山谷相当的大,准确地说应该是相当的长,xiaoT想到山谷的那头去看看,但是靠xiaoT走路的速度,到那边要n年。还好xiaoT可以买一些苹果,它把这些苹果当成动力,根据火箭发射的原理,如果xiaoT把苹果向后扔,xiaoT就会向前进。

【问题描述】

苹果有两种,一种青苹果,一种红苹果。

已知到山谷的长度为k,用一些(同一种类)苹果可以通过的路程为1。

苹果的价格是不一样的,红苹果的价格是红苹果个数的四次方。

青苹果的价格就是青苹果个数。

【输入格式】travel.in

第一行有一个正整数n表示xiaoT走路到那边需要的时间。

第二行有一个正整数k表示山谷的长度。

接下来k行,每行两个正整数,分别表示通过该段:

如果使用红苹果,则需要的数量为a

如果使用青苹果,则需要的数量为b

【输出格式】travel.out

【样例输入】

2296

3

3 1000

2 5000

4 8000

【样例输出】

2296

【样例解释】

第1段用青苹果,第2、3段用红苹果,花费是1000+(2+4)4 【数据规模】

对于30%的数据,k≤10

对于50%的数据,k≤25

对于100%的数据,k≤50

对于100%的数据,每段路消耗的红苹果的数量≤10

对于100%的数据,每段路消耗的青苹果的数量≤107

一道DP。如果直接暴搜30,剪枝50。

状态可以设f[i][j]表示前i段路,用了j个红苹果后所需的最小花费,也可以设做前i段路,用了j个红苹果后所需的最小青苹果数。

这里用第一种设法。

那么状态转移方程为:
f[i][j] = min(f[i-1][j-red[i]]+(pow(j,4)-pow(j-red[i],4)),f[i-1][j]+qing[i]) (j>=a[i])


f[i][j] = f[i-1][j]+qing[i] (j>0&&j<a[i])


边界条件:

f[0][i] = 0(不用走路不用花费)


f[k][i] = +INF


但注意INF要尽量大,数据类型也最好开到long long

由于状态转移方程只用了f数组第一维的2个,因此可以直接去掉,并倒序枚举。

时间复杂度O(n^2),空间复杂度O(n)

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
int wtf,k;
long long red[52],qing[52];

long long f[501];
int min(long long a,long long b){
if(a<b) return a;else return b;
}
int main(){
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
scanf("%d",&wtf);
scanf("%d",&k);
if (k==0){
cout<<0;
return 0;
}
int cost = 0;
for (int i = 1;i<=k;i++){
scanf("%d%d",&red[i],&qing[i]);
cost+=red[i];
}
memset(f,0,sizeof(f));
//for (int i=0;i<=501;i++)f[0][i] = 0;
for (int i=1;i<=k;i++){
for (int j =cost;j>=0;j--){
if (j>=red[i])f[j]= min(f[j-red[i]]+pow(j,4)-pow(j-red[i],4),f[j]+qing[i]);
else f[j]=f[j]+qing[i];
}
}
long long ans = 0xfffffffffff;
for (int i =1;i<=cost;i++){
ans = min(ans,f[i]);
}
cout<<ans;
}


题4 最后的战犯(maze)

【问题描述】

话说Lucky和Feli以3721部队为诱饵,歼灭了大批日军。但顽固的日军军官小犬蠢一狼(以下简称小犬)在全军覆灭之后仍然不肯认输。他躲在山区的一个岩洞中,等待日军的救援部队。他妄图凭借复杂的地形躲避我军的追踪。于是,总部派出足智多谋的Feli前往岩洞搜寻小犬。

Feli来到岩洞入口,发现岩洞其实是一个巨大的迷宫。迷宫地形极为复杂,为一个正方形,其中布满了障碍物。迷宫可以分为N*N(2≤N≤100)个区域,每个区域或者是空地,或者是不可逾越的障碍物。小犬就躲藏在其中某一个区域内。由于小犬已经忍受了几天的饥饿,Feli进入迷宫时他已经失去思维处于迷乱状态。小犬每秒钟只会沿着他的方向直线前进,如果遇到障碍物或者迷宫边界,他会立刻向右转90度(不会花去时间),继续沿直线前进(初始方向向北)。Feli每秒钟可以自主决定往哪个方向走。如果同一时刻Feli与小犬位于同一个区域,或者相邻的区域(非对角线相邻),Feli可以立刻将小犬抓住。

Feli本来打算先确定小犬的位置,然后沿最短路线抓住他,但是Feli前进时小犬同时也在移动,就不能采取这种方法了。请你帮助Feli确定一种方案,使Feli抓获小犬所用的时间最短。

【输入格式】maze.in

输入数据第一行是一个整数N。以下N行每行N个字符,“*”表示岩洞中的障碍物,“.”表示空地,“J”表示小犬(一开始他会向北走),“F”表示Feli。上北下南左西右东。

【输出格式】maze.out

输出数据仅一行,如果Feli能抓到小犬,那么输出所需的最短时间,如果Feli抓不到小犬,那么这个最后的日本战犯将在岩洞中饿死(因为Feli将在离开的时候封闭岩洞的所有出口),此时输出“No

【样例输入】

3

F*J

.*.



【样例输出】

3

这题我先用算出小犬走的10000步,然后在BFS Feil的路径,判断是否抓到。由于大概不可能有超过10000步的情况,我直接输出No Solution.

但是这题的内存只有1M,所以我的方法一个点也过不了。如果直接用数据测的话或者开大内存可以过8个点。其中有一点不得不吐槽一下:

34
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
....*****..*...*...****..*...*...
....*.F....*...*..*......*.*.....
....*****..*...*..*......**......
....*......*...*..*......*.*.....
....*.......***....****..*...*...
.................................
.................................
.................................
.................................
.****...***...****....***...*...*
..J*...*...*..*...*..*...*..**..*
...*...*****..****...*****..*.*.*
*..*...*...*..*......*...*..*..**
.**....*...*..*......*...*..*...*
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................


贴上我的代码:

#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;

const int xx[4] = {-1,0,1,0};
const int yy[4] = {0,1,0,-1};
struct position{
int x;
int y;
int t;
};
int n,fx,fy,jx,jy;
position doggo[10001];
int map[501][501];
void dogway(int nowx,int nowy,int pos,int time,int depth){
int tx = nowx+xx[pos];
int ty = nowy+yy[pos];
while(map[tx][ty]==true){
doggo[++time].x = tx;doggo[time].y = ty;depth--;
if (depth == 0)return;
tx = tx+xx[pos];
ty = ty+yy[pos];
}
nowx = tx-xx[pos];
nowy = ty-yy[pos];
int i = (pos+1)%4;
while(true){
tx = nowx+xx[i];
ty = nowy+yy[i];
if (map[tx][ty] == true){
dogway(nowx,nowy,i,time,depth);
break;
}
i = (i+1)%4;
}

}
bool check(int a,int b,int c){
if (doggo[c].x==a&&doggo[c].y==b) return true;
for (int i =0;i<4;i++){
int tx= a+xx[i];
int ty= b+yy[i];
if (doggo[c].x==tx&&doggo[c].y==ty){
return true;
}
}
return false;
}
int main(){
freopen("maze.in","r",stdin);
freopen("maze.out","w",stdout);
scanf("%d",&n);getchar();
memset(map,0,sizeof(map));
for (int i =1;i<=n;i++){
for (int j =1;j<=n;j++){
char ch;
scanf("%c",&ch);
if (ch == '*'){
map[i][j] = false;
}
if (ch == '.')map[i][j] = true;
if (ch == 'F'){
fx = i;fy= j;
map[i][j] = true;
}
if (ch == 'J'){
jx = i;jy = j;
map[i][j] = true;
}
}
getchar();
}
dogway(jx,jy,0,0,10000);
doggo[0].x = jx;doggo[0].y = jy;
queue<position>que;
position temp;
temp.x = fx;temp.y =fy;temp.t =0;
map[fx][fy] =false;
que.push(temp);
while(que.front().t<=10000){
int x = que.front().x;
int y = que.front().y;
int time = que.front().t;
que.pop();
if (check(x,y,time)){
cout<<time;
return 0;
}
else{
for (int i =0;i<4;i++){
int tx = x+xx[i];
int ty = y+yy[i];
if (map[tx][ty]== true){
map[tx][ty] = false;
temp.x = tx;temp.y =ty;temp.t =time+1;
que.push(temp);
}

}
}
}
cout<<"No solution.";
}


再贴一个1M内存下过了8个点的:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int l,r,x1,y1,x2,y2,x3,y3,xj,yj,ans,n,les[101][101];
bool tr[101][101],ma[101][101];
char a[102][102];
int dx[4]={0,1,-1,0};
int dy[4]={1,0,0,-1};
struct qw{
int xx;
int yy;
};
qw te[10001];
bool check(int x,int y)
{
if (x>0&&y>0&&x<=n&&y<=n)
if (!ma[x][y]) return true;
return false;
}
void dfs(int x,int y,int k,int di)
{
//printf("%d %d %d %d\n",x,y,k,di);
for (int i=0;i<=3;i++)
{
int x1=dx[i]+x;
int y1=dy[i]+y;
if (check(x1,y1)&&les[x1][y1]!=-1&&k>=les[x1][y1])
{
ans=k;
return;
}
}
if (les[x][y]!=-1&&k>=les[x][y])
{
ans=k;
return;
}
//if (k>10000) return;
//tim[k].xx=x;
//tim[k].yy=y;
int x0,y0;
if (di==1) x0=x-1,y0=y;
if (di==2) x0=x,y0=y+1;
if (di==3) x0=x+1,y0=y;
if (di==4) x0=x,y0=y-1;
while (!check(x0,y0))
{
di=di%4+1;
if (di==1) x0=x-1,y0=y;
if (di==2) x0=x,y0=y+1;
if (di==3) x0=x+1,y0=y;
if (di==4) x0=x,y0=y-1;
}
dfs(x0,y0,k+1,di);
}
int main()
{
freopen("maze.in","r",stdin);
freopen("maze.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",a[i]+1);
for (int j=1;j<=n;j++)
{
if (a[i][j]=='F')
{
x1=i;y1=j;
}
if (a[i][j]=='J')
{
xj=i;yj=j;
}
if (a[i][j]=='*') ma[i][j]=true;
}
}
//for (int i=1;i<=n;i++)
//for (int j=1;j<=n;j++) printf("%c",a[i][j]);
//dfs(xj,yj,0,1);
l=0;r=1;
te[1].xx=x1;
te[1].yy=y1;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
les[i][j]=-1;
les[x1][y1]=0;
tr[x1][y1]=true;
while (l<r)
{
l++;
int x3=te[l].xx;
int y3=te[l].yy;
for (int i=0;i<=3;i++)
{
int x2=te[l].xx+dx[i];
int y2=te[l].yy+dy[i];
if (check(x2,y2)&&!tr[x2][y2])
{
r++;
te[r].xx=x2;
te[r].yy=y2;
les[x2][y2]=les[x3][y3]+1;
tr[x2][y2]=true;
}
}
}
/*for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
printf("%d ",les[i][j]);
printf("\n");
}*/
if (les[xj][yj]==-1) printf("No solution.");
else
{
dfs(xj,yj,0,1);
printf("%d",ans);
}
fclose(stdin);
fclose(stdout);
}


最后附上题解:

先对feli作BFS,找出feli到每个地方的最短距离,然后模拟japan行动判断什么时候两人相遇。相遇的时间,就是花去的最短时间。当然我们得道个歉,这题内存是紧了点。不过既然是模拟赛。紧点还是有好处的吧。

Make it Manhattan

【题目描述】

混乱的城市已经变得无法控制。大楼随处乱造,城市的布局也是一片混乱。市长决定要结束这种局面,兵器并且想建造一个漂亮的、有组织的城市。

通过一些研究,他找到了使这个成为现实的理想的方法。受到纽约曼哈顿的启发,他希望所有的房子都造在矩形的格子里,用南北向的林荫道和东西向的街道隔开。这些林荫道和街道都有相同的间距。

在现在这种状况,房子已经规划在矩形格子里面了。事实上,每幢房子都恰好完全覆盖住一个矩形格子。但是,所有的楼房随机地散落在城市里,不毁坏房屋来造路是不可能的。为了使得更多的市民开心,市长想要毁坏尽量少的房子。给你当前的房屋建造状况,最小破坏量是多少呢?

上图说明了这个问题。阴影方块是原先的楼房。如果道路间距为3,上面的粗线条就是规划的道路,被经过的那个楼房就要被拆除了。 【输入】

输入文件的第一行为两个整数D和N,用一个空格隔开,分别代表道路间距、城市中的楼房数,1 <= D <= 1000, 1 <= N <= 100000。

以下N行,每行两个整数x和y,用一个空格隔开,代表房屋的位置。-109 <= x,y <= 109。 【输出】

输出文件只有一行一个整数,代表最少需要毁坏的房屋数。 【样例输入】(对应上面的图)

3 10

1 0

2 0

3 0

4 0

1 2

0 3

1 5

3 5

4 2

-2 2 【样例输出】

1

算法:

最长上升序列(nlogn):

for(int i=1;i<=top;++i)

{

pos=lower_bound(num+1,num+k+1,a[i].y)-num;//num是数组指针

if(pos>k) ++k;

num[pos]=a[i].y;

}

Treap树
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  NOIP 算法