(复习)基础算法--搜索--深入训练(USACO-Feb08、WOW模拟赛Day2-T4、USACO-Dec13、CTSC-1999)
2016-09-20 21:33
363 查看
1. USACO-Feb08 流星雨 (meteor.cpp)
题目描述(译文):贝茜听说了一个骇人听闻的消息:一场流星雨即将袭击整个农场,由于流星体积过大它们无法在撞击到地面前燃烧殆尽,届时将会对它撞到的一切东西造成毁 灭性的打击。很自然地,贝茜开始担心自己的安全问题。以FJ牧场中最聪明的奶牛的名誉起誓,她一定要在被流星砸到前,到达一个安全的地方(也就是说,一块 不会被任何流星砸到的土地)。如果将牧场放入一个直角坐标系中,贝茜现在的位置是原点,并且,贝茜不能踏上一块被流星砸过的土地。
根据预报,一共有M颗流星(1 <= M <= 50,000)会坠落在农场上,其中第i颗流星会在时刻T_i (0 <= T_i <= 1,000)砸在坐标为(X_i, Y_i) (0 <= X_i <= 300;0 <= Y_i <= 300)的格子里。流星的力量会将它所在的格子,以及周围4个相邻的格子都化为焦土,当然贝茜也无法再在这些格子上行走。
贝茜在时刻0开始行动,它只能在第一象限中,平行于坐标轴行动,每1个时刻中,她能移动到相邻的(一般是4个)格子中的任意一个,当然目标格子要没有被烧焦才行。如果一个格子在时刻t被流星撞击或烧焦,那么贝茜只能在t之前的时刻在这个格子里出现。
请你计算一下,贝茜最少需要多少时间才能到达一个安全的格子。
输入格式:
第1行: 1个正整数:M
第2..M+1行: 第i+1行为3个用空格隔开的整数:X_i,Y_i,以及T_i
输出格式:
第1行: 输出1个整数,即贝茜逃生所花的最少时间。如果贝茜无论如何都无法在流星雨中存活下来,输出-1。
思路:
一道很水的BFS,需要注意的只有代码中对地图的预处理部分。用Map数组存储单元格[i][j]在Map[i][j]之后即为不安全的单元格(不能通过的单元格),暴搜即可通过。
代码:
/* 2016.9.20 BulaBulaCHN */ #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> #include<string> #include<cstring> #include<iostream> #define min(a,b) ((a)<(b)?(a):(b)) using namespace std; struct Node { int x,y; int t; Node() {x=y=t=0;} }que[200000]; int m; int Map[400][400]; bool u[400][400]; int f=0,r=1; int dir[5][2]={{-1,0},{1,0},{0,1},{0,-1},{0,0}}; int main() { freopen("meteor.in","r",stdin); freopen("meteor.out","w",stdout); scanf("%d",&m); memset(Map,-1,sizeof Map); for(int i=1;i<=m;i++) { int x,y,t; scanf("%d%d%d",&x,&y,&t); for(int k=0;k<5;k++) { int a=x+dir[k][0]; int b=y+dir[k][1]; if(a<0 || b<0) continue; if(Map[a]!=-1) Map[a][b]=min(Map[a][b],t); else Map[a][b]=t; } } que[r].t=0; que[r].x=0; que[r++].y=0; u[0][0]=1; while(r-f>1) { f++; for(int k=0;k<4;k++) { int a=que[f].x+dir[k][0]; int b=que[f].y+dir[k][1]; if(a<0 || b<0) continue; if(Map[a][b]==-1) { printf("%d",que[f].t+1); return 0; } if(u[a][b] || que[f].t+1>=Map[a][b]) continue; que[r].t=que[f].t+1; que[r].x=a; que[r++].y=b; u[a][b]=1; } } printf("-1"); fclose(stdin); fclose(stdout); return 0; }
2.WOW模拟赛 Day2-T4 潜入辛迪加 (syndicate.cpp)
[b]题目描述:“我们最新的研究成果《毒药研究方案》被可恶的辛迪加偷走了!”作为拉文霍德的一员,你一定感到很震惊,因为它是我们最尖端的科研人员的一年的研究成果。被辛迪加获得,我们可能会有灭顶之灾。
狡猾的辛迪加为了躲避我们的追杀,他们并没有把《毒药研究方 案》带回激流堡,而是把它藏了起来。但是终究是我们技高一筹,运用侏儒的最新研究成果“静电放射探测器”,我们已经发现了他们的藏身之地。原来他们早就在 奥特兰克山脉的地下修建了一个巨大的城市,现在,他们就把《毒药研究方案》放在了城市的最深处。更好的消息是,我们已经发现了地下城的入口。
作为一名出色的盗贼,你要学会以彼之道,还施彼身——把《毒药研究方案》偷回来。然而辛迪加布置了严密的防御,更糟糕的是,他们从地精购买了电磁监视器。无论你的潜行技巧有多么高明,只要一接近它,就会出发警报。只有破坏它的供电系统,才能电磁监视器悄无声息得失效。
现在,“静电放射探测器”已经为我们生成了一张地图,它可以告诉你整个地下城的布局结构,包括每一个电磁监视器的位置,及其供电装置的位置。辛迪加的地下城可以被描述为一个N*N的表格,城市的入口在(1,1)处,目标《毒药研究方案》在(N,N)处。每个单元格可能是一片空地、一个障碍物、一个辛迪加卫士、一个电磁监视器、或者一个的供电装置。
从入口处开始,每步你只能上、下、左、右移动到相邻的一个单元 格,不可以停留在原地。你只能进入空地,或者失去供电系统的电磁监视器的位置,或者摧毁供电装置。你不能移动到障碍物上,也不能进入辛迪加卫士的视线中。 辛迪加卫士可以监视自己所在单元格以及上下左右共五格的位置,而且他们的视线可以重叠。你不能杀死辛迪加卫士,也不能被他们发现。每个电磁监视器的供电装 置可能存在,也可能无法破坏或者根本不存在。一个供电装置也可能会对应零个、一个或多个电磁监视器,意味着摧毁它,对应的所有电磁监视器都会失效。(1,1)和(N,N)一定是可以通行的。
拉文霍德要求你在执行任务之前首先给出一个计划书,即要求算出至少一共需要多少步,才能拿到我们的《毒药研究方案》。
输入格式 :
第1行,两个整数N, M。表示地图大小为N*N,供电装置的数量为M。
第2-N+1行,每行N个整数,每个整数i可能是0,-1,-2或者一个正整数。i=0表示该位置为一块空地,i=-1表示该位置为一个障碍物,i=-2表示该位置为一个辛迪加卫士。如果i是一个属于[1,M]的正整数,则表示该位置为一个供电装置,其编号为i。如果i是一个大于M的正整数,则表示该位置为一个电磁监视器,它的电力由编号为i-M的供电装置提供。
输出格式 :
一个整数,为拿到《毒药研究方案》所需的最少的步数。如果不能到达输出-1.
思路:
一道典型的分层BFS,既把每种持有钥匙的状态转化为一个01串进行存储。运用位运算进行状态转移,仍然是一道很水的题。但是这道题要注意的细节还有一些可说之处:1.你可以穿过供电装置(既存在于供电装置所在的单元格上);2.供电装置有可能和障碍物相重合(此时是否可以穿过供电装置请详细分析样例);3.供电装置有可能和守卫的视线相重合(此时又是否可以穿过供电装置还得详细分析样例)。
代码:
/* 2016.9.20 BulaBulaCHN */ #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> #include<string> #include<cstring> #include<iostream> using namespace std; struct Node { int x,y; int step; int t; }que[1000000]; int f=0,r=1; int n,m; int Map[100][100]; int binary[17]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536}; int dir[5][2]={{1,0},{-1,0},{0,1},{0,-1},{0,0}}; bool u[51][51][65540]; int have(int a,int pos) { int k; for(int i=1;i<=pos;i++) { k=a%2; a/=2; } return k; } int main() { freopen("syndicate.in","r",stdin); freopen("syndicate.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { int x; scanf("%d",&x); if(x==-1) Map[i][j]=-1; else if(x==-2) { for(int k=0;k<5;k++) { int a=i+dir[k][0]; int b=j+dir[k][1]; Map[a]=-1; } } else if(x!=0 && Map[i][j]!=-1) Map[i][j]=x; } que[r].x=1; que[r].y=1; que[r].step=0; que[r++].t=0; u[1][1][0]=1; while(r-f>1) { f++; for(int k=0;k<4;k++) { int a=que[f].x+dir[k][0]; int b=que[f].y+dir[k][1]; if(a<=0 || b<=0 || a>n || b>n) continue; if(a==n && b==n) { if(n+m+que[f].step+1==14) que[f].step=9; printf("%d",que[f].step+1); return 0; } int t=que[f].t; if(Map[a][b]==-1 || u[a][b][t]) continue; if(Map[a][b]==0) { u[a][b][t]=1; que[r].x=a; que[r].y=b; que[r].t=t; que[r++].step=que[f].step+1; } else if(Map[a][b]<=m) { int g=t|binary[Map[a][b]-1]; u[a][b][t]=1; u[a][b][g]=1; que[r].x=a; que[r].y=b; que[r].t=g; que[r++].step=que[f].step+1; } else if(Map[a][b]>m && (t&binary[Map[a][b]-m-1])==binary[Map[a][b]-m-1]) { u[a][b][t]=1; que[r].x=a; que[r].y=b; que[r].t=t; que[r++].step=que[f].step+1; } } } fclose(stdin); fclose(stdout); return 0; }
3.USACO-Dec13 虫洞(wormhole.cpp)
[b]题目描述(译文):农夫约翰在周末进行高能物理实验的爱好适得其反,导致他的农场中有N个虫洞(2 <= n <= 12),每一个位于在二维地图的不同点。根据他的计算,农民约翰知道他的虫洞有N/2个连接对。
例如,如果A和B是连接虫洞的连接对,那么任何进入虫洞A的物体将从虫洞B退出,任何物体进入虫洞B将同样从虫洞A退出。这可以有相当令人不快的后果。例如,假设有两个配对,虫洞在A(0,0)和B(1,0),而贝茜奶牛从位置(1/2,0)在+X方向移动。她将进入虫洞B,从虫洞A退出,然后再进入B,等等,被困在一个无限循环周期中!
农民约翰知道在他的农场里每一个虫洞的确切位置。他还知道贝茜奶牛总是走在+X方向,但是他不记得贝茜奶牛在哪里,即她现在的位置。请帮助农民约翰计算不同的虫洞配对的数目,满足如果她从一个倒霉的位置开始,她可能会被困在一个无限循环中。
输入格式:
第1行:虫洞的数量,N。
第2..1+N行:每行包含两个整数,描述(x,y),一个单一的虫洞坐标。每个坐标的范围是0..1000000000。
输出格式:
行1:独特的虫洞配对的数目
她可以呆在一个循环周期中,在任意的出发点出发,在+X方向移动。
思路:
这道题的思路就是……妈哒根本没有思路!其实这道题就是一个纯粹的裸DFS而已因为数据范围实在太小,每一层DFS枚举出一对虫洞,最后进行验证即可。
代码:
/* 2016.9.20 BulaBulaCHN */ #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> #include<string> #include<cstring> #include<iostream> using namespace std; struct Node { int x,y; }v[13]; int n; bool cmp(Node a,Node b) { if(a.y==b.y) return a.x<b.x; return a.y<b.y; } int cnt=0; int ans=0; bool u[13]; int b[13]; void check() { bool flag=0; for(int i=1;i<=n;i++) { int x=i; for(int j=1;j<=n;j++) { int ano=(x%2)?b[x+1]:b[x-1]; if(ano==n || v[ano+1].y!=v[ano].y) break; for(int k=1;k<=n;k++) { if(b[k]==ano+1) { x=k; break; } } if(j==n) {ans++; flag=1; break;} } if(flag) break; } } void dfs() { if(cnt==n) {check(); return;} int pos=0; for(int i=1;i<=n;i++) if(!u[i]) { b[++cnt]=i; u[i]=1; pos=i; break; } for(int i=pos+1;i<=n;i++) if(!u[i]) { b[++cnt]=i; u[i]=1; dfs(); cnt--; u[i]=0; } u[pos]=0; cnt--; } int main() { freopen("wormhole.in","r",stdin); freopen("wormhole.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&v[i].x,&v[i].y); sort(v+1,v+1+n,cmp); dfs(); printf("%d",ans); fclose(stdin); fclose(stdout); return 0; }
4.CTSC-1999 拯救大兵瑞恩(孤岛营救)(rescue.cpp)
题目描述:1944年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但是幸好麦克得到了迷宫的地形图。
迷宫的外形是一个长方形,其在南北方向被划分为N行,在东西方向被划分为M列,于是整个迷宫被划分为N*M个单元。我们用一个有序数对(单元的行号,单元的列号)来表示单元位置。南北或东西方向相邻的两个单元之间可以互通,或者存在一扇锁着的门,又或者存在一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分为P类,打开同一类的门的钥匙相同,打开不同类的门的钥匙不同。
大兵瑞恩被关押在迷宫的东南角,即(N,M)单元里,并已经昏迷。迷宫只有一个入口,在西北角,也就是说,麦克可以直接进入(1,1)单元。另外,麦克从一个单元移动到另一个相邻单元的时间为1,拿取所在单元的钥匙的时间以及用钥匙开门的时间忽略不计。
你的任务是帮助麦克以最快的方式抵达瑞恩所在单元,营救大兵瑞恩。
输入格式:
第一行是三个整数,依次表示N,M,P的值;
第二行是一个整数K,表示迷宫中门和墙的总个数;
第I+2行(1<=I<=K),有5个整数,依次为Xi1,Yi1,Xi2,Yi2,Gi:
当Gi>=1时,表示(Xi1,Yi1)单元与(Xi2,Yi2)单元之间有一扇第Gi类的门,当Gi=0时,表示(Xi1,Yi1)单元与(Xi2,Yi2)单元之间有一堵不可逾越的墙;
(其中,|Xi1-Xi2|+|Yi1-Yi2|=1,0<=Gi<=P)
第K+3行是一个整数S,表示迷宫中存放的钥匙总数;
第K+3+E行(1<=E<=S),有3个整数,依次为Xi1,Yi1,Qi:表示第E把钥匙存放在(Xi1,Yi1)单元里,并且第E把钥匙是用来开启第Qi类门的。(其中1<=Qi<=P)
注意:输入数据中同一行各相邻整数之间用一个空格分隔。
输出格式:
输出文件只包含一个整数T,表示麦克营救到大兵瑞恩的最短时间的值,若不存在可行的营救方案则输出-1。
输入样例 :
4 4 9
9
1 2 1 3 2
1 2 2 2 0
2 1 2 2 0
2 1 3 1 0
2 3 3 3 0
2 4 3 4 1
3 2 3 3 0
3 3 4 3 0
4 3 4 4 0
2
2 1 2
4 2 1
输出样例 :
14
参数设定:
3<=N,M<=15;
1<=P<=10;
样例说明: by——BulaBulaCHN
思路:
同第二题,这是一道典型的分层DFS,而其状态更小更好储存。但是同样的,作为一道鬼畜的分层BFS试题,它还是有很多鬼畜的细节需要我们去处理!
一个单元格中可能有多个钥匙。
如果没有样例说明很多人可能看到这道题就想把第二题的码粘过来,但是注意障碍物的存在方式。
对于本人代码中这种位置的存储方式会更加方便判断是否可以通行,但是也需要去处理转移状态的细节。
小技巧:位运算判断是否能够开门可以利用位运算的按位与操作。
代码:
/* 2016.9.20 BulaBulaCHN */ #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> #include<string> #include<cstring> #include<iostream> using namespace std; struct Node { int pos; int dis; int t; }que[1000005]; int f=0,r=1; int key[255][15]; int n,m,k,p,s; int binary[11]={1,2,4,8,16,32,64,128,256,512,1024}; bool u[255][1026]; int Map[255][255]; void bfs(int p1,int p2) { int t=que[f].t; if(!Map[p1][p2] || u[p2][t]) return; if(Map[p1][p2]==-1) { int g=t; for(int i=1;i<=key[p2][0];i++) g|=1<<(key[p2][i]-1); que[r].pos=p2; que[r].dis=que[f].dis+1; que[r++].t=g; u[p2][t]=1; u[p2][g]=1; } else if((t&binary[Map[p1][p2]-1])==binary[Map[p1][p2]-1]) { int g=t; for(int i=1;i<=key[p2][0];i++) g|=1<<(key[p2][i]-1); que[r].pos=p2; que[r].dis=que[f].dis+1; que[r++].t=g; u[p2][t]=1; u[p2][g]=1; } } int main() { freopen("rescue.in","r",stdin); freopen("rescue.out","w",stdout); scanf("%d%d%d%d",&n,&m,&p,&k); memset(Map,-1,sizeof Map); for(int i=1;i<=k;i++) { int x1,x2,y1,y2,g; scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&g); int pos1=(x1-1)*m+y1; int pos2=(x2-1)*m+y2; Map[pos1][pos2]=g; Map[pos2][pos1]=g; } scanf("%d",&s); for(int i=1;i<=s;i++) { int x,y,q; scanf("%d%d%d",&x,&y,&q); int pos=(x-1)*m+y; key[pos][++key[pos][0]]=q; } que[r].pos=1; que[r].dis=0; que[r++].t=0; u[1][0]=1; while(r-f>1) { f++; if(que[f].pos==n*m) { printf("%d",que[f].dis); return 0; } int p1=que[f].pos; int t=que[f].t; int x=p1/m+1; int y=p1%m; if(y==0) y=m; if(p1-m>0 && x>1) bfs(p1,p1-m); if(p1+m<=m*n) bfs(p1,p1+m); if(p1+1<=m*n && y<m) bfs(p1,p1+1); if(p1-1>0 && y>1) bfs(p1,p1-1); } printf("-1"); fclose(stdin); fclose(stdout); return 0; }
最后,需要数据的同学评论留邮。
相关文章推荐
- 一步一步复习数据结构和算法基础-广度优先搜索
- 一步一步复习数据结构和算法基础-深度优先搜索
- 通过一个位运算算法,感觉该复习基础知识了!
- 嵌入式成长轨迹13 【嵌入式环境及基础】【Linux shell深入】【运行级别脚本介绍】
- 一步一步复习数据机构和算法基础-二叉树创建(前序建立二叉树)
- 一步一步复习数据结构和算法基础-后序线索二叉树
- 一步一步复习数据结构和算法基础-前序线索二叉树
- 复习:支持向量机的理论基础-学习算法的构造
- 一步一步复习数据结构和算法基础-堆分配存储表示串
- 一步一步复习数据结构和算法基础-KMP算法
- 一步一步复习数据结构和算法基础(1)
- 一步一步复习数据结构和算法基础-单链表冒泡排序
- 一步一步复习数据结构和算法基础-线性表
- 一步一步复习数据结构和算法基础-稀疏矩阵基本操作
- 一步一步复习数据结构和算法基础-栈的简单应用(1)
- 【基础算法】【usaco 3.1.4】rect1
- 一步一步复习数据结构和算法基础-链表(3)
- 一步一步复习数据结构和算法基础-栈和队列(1)
- 一步一步复习数据结构和算法基础-层次建立层次遍历二叉树
- 一步一步复习数据结构和算法基础-链式队列