Monthly Archives: November 2014

POJ 1753-Flip Game

POJ 1753-Flip Game Flip Game Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 31655 Accepted: 13776 Description Flip game is played on a rectangular 4×4 field with two-sided pieces placed on each of its 16 squares. One side of each piece is white and the other one is black and each piece is lying either it’s black or white side up. Each round you flip 3 to 5 pieces, thus changing the color of their upper side from black to white and vice versa. The pieces to be flipped are chosen every round according to the following rules: Choose any one of the 16 pieces. Flip the chosen piece and also all adjacent pieces to the left, to the right, to the top, and to the bottom of the chosen piece (if there are any). Consider the following position as an example: bwbw wwww bbwb bwwb Here “b” denotes pieces lying their black side up and “w” denotes pieces lying their white side up. If we choose to flip the 1st piece from the 3rd row (this choice is shown at the picture), then the field will become: bwbw bwww wwwb wwwb The goal of the game is to flip either all pieces white side up or all pieces black side up. You are to write a program that will search for the minimum number of rounds needed to achieve this goal. Input The input consists of 4 lines with 4 characters “w” or “b” each that denote game field position. Output Write to the output file a single integer number – the minimum number of rounds needed to achieve the goal of the game from the given position. If the goal is initially achieved, then write 0. If it’s impossible to achieve the goal, then write the word “Impossible” (without quotes). Sample Input bwwb bbwb bwwb bwww Sample Output 4 Source Northeastern Europe 2000


题意:翻动格子使得整个棋盘为全黑或者全白,求最少的翻动次数,翻动某一个格子,也会同时翻动其周围的四个格子(如果有的话)。因为某一个格子翻动2次的结果和不翻是一样的;翻动3次的结果和翻动1次的结果是一样的;多个也是一样,所以一个格子最多翻动1次,这样总的翻动次数最多为16次。每个格子有翻和不翻两种情况,所以总共有$$2^{16}$$种情况,可以暴力枚举。 但是一直没有想好用哪种方法,深度搜索还是广度搜索。后来想了想,这是求最优解(最少翻动次数)的题,所以用广度优先搜索的效率应该会高一些,但是在BFS的过程中曾有一个问题困扰了,后面再说。又因为棋盘正好是4*4=16的,可以用int的位运算来表示,b表示1,w表示0,这样棋盘的一个状态就可以用一个int数来表示,翻动格子也可以用位运算来表示,总共的状态数是$$2^{16}$$,取值范围是[0,65535],当棋盘状态是0或者65535时,表示全白或者全黑,这时找到结果。 了解了这些,就可以写出BFS+位运算的代码了,这题我是参考了网上的题解: [cpp] #include<iostream> #include<queue> using namespace std; const int MAX_STATUS=65536;//2^16=65536种,所以[0,65535] const int ALL_0=0;//全0 const int ALL_1=65535;//全1 int ans; int visited[MAX_STATUS]={0};//访问标记 typedef struct POS//position翻转位置 { int val;//当前翻转状态的值 int step;//到当前状态共翻转了多少次 }; //广搜 void BFS(int init_id) { queue<POS> Q_P; POS s,next_p; //初始位置 s.val=init_id; s.step=0; visited[s.val]=1; Q_P.push(s); while(!Q_P.empty()) { s=Q_P.front(); Q_P.pop(); if(s.val==ALL_0||s.val==ALL_1)//找到结果 { ans=s.step; return; } //尝试翻16个位置 for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { next_p.val=s.val; //测试i,翻转[i,j]上下位置的格子 if(i==0) next_p.val^=1<<(11-4*i-j); else if(i==3) next_p.val^=1<<(19-4*i-j); else { next_p.val^=1<<(11-4*i-j); next_p.val^=1<<(19-4*i-j); } //测试j,翻转[i,j]左右位置的格子 if(j==0) next_p.val^=3<<(14-4*i-j); else if(j==3) next_p.val^=3<<(15-4*i-j); else next_p.val^=7<<(14-4*i-j); if(visited[next_p.val]==0)//如果没有访问 { visited[next_p.val]=1;//访问 next_p.step=s.step+1;//翻转次数加1 Q_P.push(next_p);//并加入队列 } } } } ans=-1;//到这里还没有return,说明impossible } int main() { //freopen("input.txt","r",stdin); char c; int id=0; //输入转换 /*bwbw wwww bbwb bwwb 排列成bwbw wwww bbwb bwwb,然后把b换成1,w换成0*/ for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { cin>>c; id<<=1; if(c==’b’)//b=1;w=0 id+=1; } } BFS(id); if(ans==-1) cout<<"Impossible"; else cout<<ans; return 0; } [/cpp] 本代码提交AC,用时79MS,内存508K。 在每一次BFS的时候,我们都要尝试去翻所有16个格子,如果翻动之后达到了一个新的状态(也就是之前没有访问过),说明这个翻动是有效的,把它加入到队列中。在翻动的时候,怎样进行位运算大家可以把i=0,j=0代入到for循环里手算一遍,程序会根据i和j的不同,和不同的数进行异或运算。 之前我说有一个地方让我困惑,就是我之前:第一次BFS的时候肯定把所有格子都翻了一遍,也就是把(0,0)(0,1)…(3,3)翻动的结果都加入到队列中了,那么在第二次BFS的时候,把(0,0)出队,这个时候再来翻所有16个格子,这样不就相当于把所有格子又翻回到初始状态了吗?其实这种想法是不对的,说到底还是对BFS理解不够透彻。**我们在每次BFS的时候都是在队首元素的基础上翻转的,因为我们在while开头执行了s=Q_P.front();Q_P.pop();这么两句**,那么当我们把(0,0)翻转状态出队在去翻转的时候,是在(0,0)翻转状态的基础是再进行翻转的,而这个时候是只有(0,0)这一个格子翻了,其他15个格子都没有翻;同理当把(0,1)出队的时候,也是只有(0,1)翻了,其他15个都没有翻。所以这样BFS下去是能够搜索到所有状态的。 纵观所有BFS题目,套路都是一样的。]]>

hihoCoder 1037-数字三角形

hihoCoder 1037-数字三角形 #1037 : 数字三角形 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 问题描述 小Hi和小Ho在经历了螃蟹先生的任务之后被奖励了一次出国旅游的机会,于是他们来到了大洋彼岸的美国。美国人民的生活非常有意思,经常会有形形色色、奇奇怪怪的活动举办,这不,小Hi和小Ho刚刚下飞机,就赶上了当地的迷宫节活动。迷宫节里展览出来的迷宫都特别的有意思,但是小Ho却相中了一个其实并不怎么像迷宫的迷宫——因为这个迷宫的奖励非常丰富~ 于是小Ho找到了小Hi,让小Hi帮助他获取尽可能多的奖品,小Hi把手一伸道:“迷宫的介绍拿来!” 小Ho选择的迷宫是一个被称为“数字三角形”的n(n不超过200)层迷宫,这个迷宫的第i层有i个房间,分别编号为1..i。除去最后一层的房间,每一个房间都会有一些通往下一层的房间的楼梯,用符号来表示的话,就是从第i层的编号为j的房间出发会有两条路,一条通向第i+1层的编号为j的房间,另一条会通向第i+1层的编号为j+1的房间,而最后一层的所有房间都只有一条离开迷宫的道路。这样的道路都是单向的,也就是说当沿着这些道路前往下一层的房间或者离开迷宫之后,小Ho没有办法再次回到这个房间。迷宫里同时只会有一个参与者,而在每个参与者进入这个迷宫的时候,每个房间里都会生成一定数量的奖券,这些奖券可以在通过迷宫之后兑换各种奖品。小Ho的起点在第1层的编号为1的房间,现在小Ho悄悄向其他参与者弄清楚了每个房间里的奖券数量,希望小Hi帮他计算出他最多能获得多少奖券。 提示一:盲目贪心不可取,搜索计算太耗时 提示二:记忆深搜逞神威,宽度优先解难题 提示三:总结归纳提公式,减少冗余是真理 输入 每个测试点(输入文件)有且仅有一组测试数据。 每组测试数据的第一行为一个正整数n,表示这个迷宫的层数。 接下来的n行描述这个迷宫中每个房间的奖券数,其中第i行的第j个数代表着迷宫第i层的编号为j的房间中的奖券数量。 测试数据保证,有100%的数据满足n不超过100 对于100%的数据,迷宫的层数n不超过100 对于100%的数据,每个房间中的奖券数不超过1000 对于50%的数据,迷宫的层数不超过15(小Ho表示2^15才3万多呢,也就是说……) 对于10%的数据,迷宫的层数不超过1(小Hi很好奇你的边界情况处理的如何?~) 对于10%的数据,迷宫的构造满足:对于90%以上的结点,左边道路通向的房间中的奖券数比右边道路通向的房间中的奖券数要多。 对于10%的数据,迷宫的构造满足:对于90%以上的结点,左边道路通向的房间中的奖券数比右边道路通向的房间中的奖券数要少。 输出 对于每组测试数据,输出一个整数Ans,表示小Ho可以获得的最多奖券数。 样例输入 5 2 6 4 1 2 8 4 0 9 6 6 5 5 3 6 样例输出 28


简单的动态规划问题,根据题目的描述:
从第i层的编号为j的房间出发会有两条路,一条通向第i+1层的编号为j的房间,另一条会通向第i+1层的编号为j+1的房间
假设best[i][j]为到达第i层第j个房间时能获得的最大奖券数,则有状态转换方程: $$best[i][j]=max \{ best[i-1][j],best[i-1][j-1] \} +reward[i][j] $$ 然后求最后一层的最大值,就是最终能够获取到的最大奖券数。 完整代码如下: [cpp] #include<iostream> using namespace std; int n; int reward[101][101];//reward[i][j]表示第i层第j个房间的奖券数 int best[101];//best[j]表示到达第i层第j个房间时能获取到的最大奖券数 int main() { //freopen("input.txt","r",stdin); cin>>n; for(int i=0;i<n;i++) for(int j=0;j<=i;j++) cin>>reward[i][j]; best[0]=reward[0][0]; for(int i=1;i<n;i++) { for(int j=i;j>=0;j–)//从右往左填表,这样只需要一行空间 { if(j==0) best[j]+=reward[i][j]; else { best[j]=(best[j]>best[j-1]?best[j]:best[j-1])+reward[i][j]; } } } int ans=0; for(int i=0;i<n;i++) if(best[i]>ans)//求最大值 ans=best[i]; cout<<ans; return 0; } [/cpp] 本代码提交AC,用时1MS,内存0MB。]]>

POJ 1045-Bode Plot

POJ 1045-Bode Plot Bode Plot Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 12888 Accepted: 8184 Description Consider the AC circuit below. We will assume that the circuit is in steady-state. Thus, the voltage at nodes 1 and 2 are given by v1 = VS coswt and v2 = VRcos (wt + q ) where VS is the voltage of the source, w is the frequency (in radians per second), and t is time. VR is the magnitude of the voltage drop across the resistor, and q is its phase. ![](http://poj.org/images/1045/bode.jpg) You are to write a program to determine VR for different values of w. You will need two laws of electricity to solve this problem. The first is Ohm’s Law, which states v2 = iR where i is the current in the circuit, oriented clockwise. The second is i = C d/dt (v1-v2) which relates the current to the voltage on either side of the capacitor. “d/dt”indicates the derivative with respect to t. Input The input will consist of one or more lines. The first line contains three real numbers and a non-negative integer. The real numbers are VS, R, and C, in that order. The integer, n, is the number of test cases. The following n lines of the input will have one real number per line. Each of these numbers is the angular frequency, w. Output For each angular frequency in the input you are to output its corresponding VR on a single line. Each VR value output should be rounded to three digits after the decimal point. Sample Input 1.0 1.0 1.0 9 0.01 0.031623 0.1 0.31623 1.0 3.1623 10.0 31.623 100.0 Sample Output 0.010 0.032 0.100 0.302 0.707 0.953 0.995 1.000 1.000 Source Greater New York 2001


这是一道数学+物理题,计算$$V_R$$的过程如下: 已知: ①$$v_1 = V_Scoswt$$ ②$$v_2 = V_R cos (wt+\theta )$$ ③$$v_2 = iR$$ ④$$i = C d/dt (v_1-v_2)$$ 把④代入③得⑤:$$v_2 =CR d/dt (v_1-v_2)$$ 再把①和②代入⑤得⑥:$$v_2=CR d/dt (V_Scoswt-V_R cos (wt+\theta ))$$ $$d/dt$$为对$$t$$求导,所以把⑥中的$$coswt$$和$$cos(wt+\theta )$$对$$t$$求导得到⑦:$$v_2=CRw(V_Rsin(wt+\theta )-V_Ssinwt)$$ 又因为②式,所以得到⑧:$$V_R cos (wt+\theta )=CRw(V_Rsin(wt+\theta )-V_Ssinwt)$$ 令$$wt+\theta =0$$,⑧式变为⑨:$$V_R=CRw(V_Ssin\theta)$$ 令$$t=0$$,⑧式变为⑩:$$tan\theta=1/CRw$$ 简单画一个三角形: 快速得到$$sin\theta=1/\sqrt{1+(CRw)^2}$$,代入⑨式得到最终结果:$$V_R=CRwV_S/\sqrt{1+(CRw)^2}$$ 得到了公式,快速写出代码: [cpp] #include<iostream> #include<cmath> #include<cstdio> using namespace std; int main() { //freopen("input.txt","r",stdin); double vs,r,c,w,vr; int n; cin>>vs>>r>>c>>n; while(n–) { cin>>w; vr=c*r*w*vs/sqrt(1+c*r*w*c*r*w); printf("%.3lf\n",vr); } return 0; } [/cpp] 本代码提交AC,用时0MS,内存208K。 ]]>

POJ 1837-Balance

POJ 1837-Balance Balance Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 10845 Accepted: 6742 Description Gigel has a strange “balance” and he wants to poise it. Actually, the device is different from any other ordinary balance. It orders two arms of negligible weight and each arm’s length is 15. Some hooks are attached to these arms and Gigel wants to hang up some weights from his collection of G weights (1 <= G <= 20) knowing that these weights have distinct values in the range 1..25. Gigel may droop any weight of any hook but he is forced to use all the weights. Finally, Gigel managed to balance the device using the experience he gained at the National Olympiad in Informatics. Now he would like to know in how many ways the device can be balanced. Knowing the repartition of the hooks and the set of the weights write a program that calculates the number of possibilities to balance the device. It is guaranteed that will exist at least one solution for each test case at the evaluation. Input The input has the following structure: • the first line contains the number C (2 <= C <= 20) and the number G (2 <= G <= 20); • the next line contains C integer numbers (these numbers are also distinct and sorted in ascending order) in the range -15..15 representing the repartition of the hooks; each number represents the position relative to the center of the balance on the X axis (when no weights are attached the device is balanced and lined up to the X axis; the absolute value of the distances represents the distance between the hook and the balance center and the sign of the numbers determines the arm of the balance to which the hook is attached: ‘-‘ for the left arm and ‘+’ for the right arm); • on the next line there are G natural, distinct and sorted in ascending order numbers in the range 1..25 representing the weights’ values. Output The output contains the number M representing the number of possibilities to poise the balance. Sample Input 2 4 -2 3 3 4 5 8 Sample Output 2 Source Romania OI 2002


这一题题意为:给定一个天平,在天平两个臂上有若干个挂钩,给定若干砝码,问将所有砝码都挂上挂钩并使天平平衡的方法数有多少种?这一题有一定的难度,如果只有两个挂钩,问题比较好解决,可以看我的另一篇博客从一个数字序列中取若干个数字使得和为某个数,问共有多少种取数方案;但是这一题的挂钩有若干个,情况比较多,想过用动态规划、背包问题,但是一直想不出状态转换方程。 后来看了某前辈的解题报告,他提出了一个平衡度的概念,并推导出如下的状态转换方程:①dp[i][ j+ w[i]*c[k] ]= ∑(dp[i-1][j])。dp[i][j]表示前i个砝码使平衡度为j共有多少种方案数。 按照常理,我们得到的状态转换方程为:②dp[i][j] =∑(dp[i – 1][j – c[i] * w[i]]),表示dp[i][j]等于前i-1个砝码使平衡度为j-x的方案数之和,这个x就是第i个砝码挂在c个挂钩中的某一个产生的力臂。稍加推导就能得出①等价于②。 有了状态转换方程,我们可以很快的写出代码: [cpp] #include<iostream> using namespace std; int c[21]; int w[21]; int dp[21][15001]; int main() { int n,g; cin>>n>>g; for(int i=1;i<=n;i++) cin>>c[i]; for(int i=1;i<=g;i++) cin>>w[i]; dp[0][7500]=1;//不挂任何砝码的时候它本身就平衡了,所以是一种“挂法” for(int i=1;i<=g;i++)//对于每一个砝码 { for(int j=0;j<=15000;j++)//挂上去之后可能使天平出现0-15000中的任意一种状态 { if(dp[i-1][j])//如果这个状态之前出现过,则直接用 { for(int k=1;k<=n;k++) { dp[i][j+w[i]*c[k]]+=dp[i-1][j]; } } } } cout<<dp[g][7500]<<endl; return 0; } [/cpp] 代码提交AC,用时0MS,内存1060K。]]>

POJ 2676-Sudoku

POJ 2676-Sudoku Sudoku Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 14104 Accepted: 6963 Special Judge Description Sudoku is a very simple task. A square table with 9 rows and 9 columns is divided to 9 smaller squares 3×3 as shown on the Figure. In some of the cells are written decimal digits from 1 to 9. The other cells are empty. The goal is to fill the empty cells with decimal digits from 1 to 9, one digit per cell, in such way that in each row, in each column and in each marked 3×3 subsquare, all the digits from 1 to 9 to appear. Write a program to solve a given Sudoku-task. Input The input data will start with the number of the test cases. For each test case, 9 lines follow, corresponding to the rows of the table. On each line a string of exactly 9 decimal digits is given, corresponding to the cells in this line. If a cell is empty it is represented by 0. Output For each test case your program should print the solution in the same format as the input data. The empty cells have to be filled according to the rules. If solutions is not unique, then the program may print any one of them. Sample Input 1 103000509 002109400 000704000 300502006 060000050 700803004 000401000 009205800 804000107 Sample Output 143628579 572139468 986754231 391542786 468917352 725863914 237481695 619275843 854396127 Source Southeastern Europe 2005


这一题是练习深度搜索和剪枝的好题目。题意很简答,数独游戏,在每一行、每一列和每一个小正方形填入1-9这9个数字,数字不能重复,求一种可以的方案。 因为只需要求一种可行解,根据POJ Problem 3278: Catch That Cow后面的总结,很快有了深度搜索的思路:从上到下,从左到右依次扫描每一个格子,如果这个格子上的数字不是0,说明原来就有数字了,不用填,测试下一个格子;如果这个格子为0,则求出可填入该格子的候选数字,再依次测试每一个候选数字是否可行,一直这样深度搜索下去,直到将所有格子测试完毕。这个思路相信很多人都能很快想到,完整的代码如下: [cpp] #include<iostream> #include<vector> using namespace std; const int N=9; int board[N][N];//棋盘 int candidates[N];//对于某一个坐标ij,可以填入的候选数字 //给定坐标(row,column),求它所在的小方格的左上角坐标 void get_subquare_start(int row,int column,int& x0,int& y0) { if(row<3&&column<3) { x0=0; y0=0; } else if(row<3&&column<6) { x0=0; y0=3; } else if(row<3&&column<9) { x0=0; y0=6; } else if(row<6&&column<3) { x0=3; y0=0; } else if(row<6&&column<6) { x0=3; y0=3; } else if(row<6&&column<9) { x0=3; y0=6; } else if(row<9&&column<3) { x0=6; y0=0; } else if(row<9&&column<6) { x0=6; y0=3; } else if(row<9&&column<9) { x0=6; y0=6; } } //给定坐标(row,column),将可以填入该坐标的候选数字放入vi //思路就是先把candidates[N]填满1-9,然后将给定坐标的 //横轴、纵轴、小方格出现过的数字去掉,剩下的数字就是 //候选数字 void get_candidates(int row,int column,vector<int>& vi) { for(int i=0;i<N;i++) candidates[i]=i+1; for(int i=0;i<N;i++)//横轴 { if(board[row][i]!=0) candidates[board[row][i]-1]=0; } for(int i=0;i<N;i++)//纵轴 { if(board[i][column]!=0) candidates[board[i][column]-1]=0; } int x0,y0; get_subquare_start(row,column,x0,y0); for(int i=x0;i<x0+3;i++)//小方格 { for(int j=y0;j<y0+3;j++) { if(board[i][j]!=0) candidates[board[i][j]-1]=0; } } for(int i=0;i<N;i++)//重复工作,可优化 if(candidates[i]!=0) vi.push_back(candidates[i]); } //深度搜索 bool dfs(int pos) { if(pos==N*N) { return true; } int row=pos/N; int column=pos%N; if(board[row][column]!=0)//如果有数字 return dfs(pos+1);//则搜下一个格子 else { vector<int> local_candidates;//要使用局部的candidates,因为如果是全局的,那么回溯的时候candidates已经修改了 get_candidates(row,column,local_candidates);//获取可填入该格子的候选数字 int cand_num=local_candidates.size(); for(int i=0;i<cand_num;i++) { board[row][column]=local_candidates[i];//填入 if(dfs(pos+1))//继续搜下一个格子 return true; } board[row][column]=0;//回溯 return false;//回溯 } } int main() { freopen("input.txt","r",stdin); int t; char c; cin>>t; while(t–) { for(int i=0;i<N;i++) { for(int j=0;j<N;j++) { cin>>c; board[i][j]=c-‘0’; } } if(dfs(0)) { for(int i=0;i<N;i++) { for(int j=0;j<N;j++) cout<<board[i][j]; cout<<endl; } } } return 0; } [/cpp] 满心欢喜的提交后发现TLE超时了,此时泪流满面,后来又琢磨了一下,师兄给的分类是简单搜索技巧和剪枝,我上面的代码压根就没有剪枝,TLE也就不足为奇了。 那么怎样剪枝?想一想我们手算解数独的过程,正常情况下,如果A这个格子有5个候选数字可以填入,而B这个格子只有2个候选数字可以填入,我们肯定会先填好B这个格子,再填A,因为B的情况少一些,而填完B之后,A的情况可能又会减少,这样比先填A再填B会快得多。根据这样的思路,我们在每次深度搜索的时候,把每个空格的候选数字求出来,并求候选数字个数最少的那个格子C,填格子C,继续深度搜索,直到所有空格填满为止。 可能有的同学会问,每次深度搜索的时候都要求一遍所有空格的候选数字,这样会不会耗时太多了,这个我也担心过,但是总体来说,剪枝比第一个版本的代码高效多了。下面是剪枝版本的完整代码: [cpp] #include<iostream> #include<vector> using namespace std; const int N=9; int board[N][N];//棋盘 int candidates[N];//对于某一个坐标ij,可以填入的候选数字 //给定坐标(row,column),求它所在的小方格的左上角坐标 void get_subquare_start(int row,int column,int& x0,int& y0) { if(row<3&&column<3) { x0=0; y0=0; } else if(row<3&&column<6) { x0=0; y0=3; } else if(row<3&&column<9) { x0=0; y0=6; } else if(row<6&&column<3) { x0=3; y0=0; } else if(row<6&&column<6) { x0=3; y0=3; } else if(row<6&&column<9) { x0=3; y0=6; } else if(row<9&&column<3) { x0=6; y0=0; } else if(row<9&&column<6) { x0=6; y0=3; } else if(row<9&&column<9) { x0=6; y0=6; } } //给定坐标(row,column),将可以填入该坐标的候选数字放入vi //思路就是先把candidates[N]填满1-9,然后将给定坐标的 //横轴、纵轴、小方格出现过的数字去掉,剩下的数字就是 //候选数字 void get_candidates(int row,int column,vector<int>& vi) { for(int i=0;i<N;i++) candidates[i]=i+1; for(int i=0;i<N;i++)//横轴 { if(board[row][i]!=0) candidates[board[row][i]-1]=0; } for(int i=0;i<N;i++)//纵轴 { if(board[i][column]!=0) candidates[board[i][column]-1]=0; } int x0,y0; get_subquare_start(row,column,x0,y0); for(int i=x0;i<x0+3;i++)//小方格 { for(int j=y0;j<y0+3;j++) { if(board[i][j]!=0) candidates[board[i][j]-1]=0; } } for(int i=0;i<N;i++)//重复工作,可优化 if(candidates[i]!=0) vi.push_back(candidates[i]); } //深度搜索 bool dfs(vector<int>& blank_pos) { int blank_num=blank_pos.size(); if(blank_num==0)//如果空格都填完了,返回true { return true; } int row,column,pos; int min_candidates=N+1; vector<int> global_candidates; for(int i=0;i<blank_num;i++) { row=blank_pos[i]/N; column=blank_pos[i]%N; vector<int> local_candidates; get_candidates(row,column,local_candidates);//对于每一个空格求可填入的候选数字 if(local_candidates.size()<min_candidates)//求到最小值 { pos=blank_pos[i];//记录位置 min_candidates=local_candidates.size(); global_candidates.clear(); global_candidates=local_candidates;//vector可以直接赋值 } } row=pos/N; column=pos%N; vector<int> next_blank_pos; for(int j=0;j<blank_num;j++) if(blank_pos[j]!=pos) next_blank_pos.push_back(blank_pos[j]);//剩余的空格 for(int i=0;i<min_candidates;i++) { board[row][column]=global_candidates[i];//填入 if(dfs(next_blank_pos))//继续搜下一个格子 return true; } board[row][column]=0;//回溯 return false;//回溯 } //剪枝版本,AC,用时282MS,内存240K int main() { //freopen("input.txt","r",stdin); int t; char c; cin>>t; vector<int> blank_pos; while(t–) { blank_pos.clear();//因为可能有多个case,所以每次都要clear!!! for(int i=0;i<N;i++) { for(int j=0;j<N;j++) { cin>>c; board[i][j]=c-‘0’; if(board[i][j]==0) blank_pos.push_back(i*N+j); } } if(dfs(blank_pos)) { for(int i=0;i<N;i++) { for(int j=0;j<N;j++) cout<<board[i][j]; cout<<endl; } } } return 0; } [/cpp] 这个版本的代码需要注意一个小细节,我在每次输入的时候都将空格的位置记录在了blank_pos的vector中,因为题目说有多个case,所以每次输入的时候都要记得blank_pos.clear(),要不然会影响下一个case。这个版本的代码AC,用时282MS,内存240K。 AC之后,又看了一下discuss,发现很多同学谈到如果倒搜,会比顺搜快得多,倒搜就是从最后一个格子开始搜索。闲来无事,我就把第一个版本的代码改成了倒搜索,只需要修改两三个地方,代码如下: [cpp] #include<iostream> #include<vector> using namespace std; const int N=9; int board[N][N];//棋盘 int candidates[N];//对于某一个坐标ij,可以填入的候选数字 //给定坐标(row,column),求它所在的小方格的左上角坐标 void get_subquare_start(int row,int column,int& x0,int& y0) { if(row<3&&column<3) { x0=0; y0=0; } else if(row<3&&column<6) { x0=0; y0=3; } else if(row<3&&column<9) { x0=0; y0=6; } else if(row<6&&column<3) { x0=3; y0=0; } else if(row<6&&column<6) { x0=3; y0=3; } else if(row<6&&column<9) { x0=3; y0=6; } else if(row<9&&column<3) { x0=6; y0=0; } else if(row<9&&column<6) { x0=6; y0=3; } else if(row<9&&column<9) { x0=6; y0=6; } } //给定坐标(row,column),将可以填入该坐标的候选数字放入vi //思路就是先把candidates[N]填满1-9,然后将给定坐标的 //横轴、纵轴、小方格出现过的数字去掉,剩下的数字就是 //候选数字 void get_candidates(int row,int column,vector<int>& vi) { for(int i=0;i<N;i++) candidates[i]=i+1; for(int i=0;i<N;i++)//横轴 { if(board[row][i]!=0) candidates[board[row][i]-1]=0; } for(int i=0;i<N;i++)//纵轴 { if(board[i][column]!=0) candidates[board[i][column]-1]=0; } int x0,y0; get_subquare_start(row,column,x0,y0); for(int i=x0;i<x0+3;i++)//小方格 { for(int j=y0;j<y0+3;j++) { if(board[i][j]!=0) candidates[board[i][j]-1]=0; } } for(int i=0;i<N;i++)//重复工作,可优化 if(candidates[i]!=0) vi.push_back(candidates[i]); } //深度搜索 bool dfs(int pos) { if(pos==-1)//倒搜到-1结束 { return true; } int row=pos/N; int column=pos%N; if(board[row][column]!=0)//如果有数字 return dfs(pos-1);//则搜下一个格子 else { vector<int> local_candidates;//要使用局部的candidates,因为如果是全局的,那么回溯的时候candidates已经修改了 get_candidates(row,column,local_candidates);//获取可填入该格子的候选数字 int cand_num=local_candidates.size(); for(int i=0;i<cand_num;i++) { board[row][column]=local_candidates[i];//填入 if(dfs(pos-1))//继续搜下一个格子 return true; } board[row][column]=0;//回溯 return false;//回溯 } } //倒搜版本,AC,用时125MS,内存224K int main() { //freopen("input.txt","r",stdin); int t; char c; cin>>t; while(t–) { for(int i=0;i<N;i++) { for(int j=0;j<N;j++) { cin>>c; board[i][j]=c-‘0’; } } if(dfs(N*N-1))//倒搜 { for(int i=0;i<N;i++) { for(int j=0;j<N;j++) cout<<board[i][j]; cout<<endl; } } } return 0; } [/cpp] 令我吃惊的是,倒搜这个版本的代码AC了,而且用时125MS,内存224K,居然比剪枝还要快,内存占用也更少。到现在我还没有搞清楚更快的原因。]]>

POJ 1028-Web Navigation

POJ 1028-Web Navigation Web Navigation Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 29660 Accepted: 13249 Description Standard web browsers contain features to move backward and forward among the pages recently visited. One way to implement these features is to use two stacks to keep track of the pages that can be reached by moving backward and forward. In this problem, you are asked to implement this. The following commands need to be supported: BACK: Push the current page on the top of the forward stack. Pop the page from the top of the backward stack, making it the new current page. If the backward stack is empty, the command is ignored. FORWARD: Push the current page on the top of the backward stack. Pop the page from the top of the forward stack, making it the new current page. If the forward stack is empty, the command is ignored. VISIT : Push the current page on the top of the backward stack, and make the URL specified the new current page. The forward stack is emptied. QUIT: Quit the browser. Assume that the browser initially loads the web page at the URL http://www.acm.org/ Input Input is a sequence of commands. The command keywords BACK, FORWARD, VISIT, and QUIT are all in uppercase. URLs have no whitespace and have at most 70 characters. You may assume that no problem instance requires more than 100 elements in each stack at any time. The end of input is indicated by the QUIT command. Output For each command other than QUIT, print the URL of the current page after the command is executed if the command is not ignored. Otherwise, print “Ignored”. The output for each command should be printed on its own line. No output is produced for the QUIT command. Sample Input VISIT http://acm.ashland.edu/ VISIT http://acm.baylor.edu/acmicpc/ BACK BACK BACK FORWARD VISIT http://www.ibm.com/ BACK BACK FORWARD FORWARD FORWARD QUIT Sample Output http://acm.ashland.edu/ http://acm.baylor.edu/acmicpc/ http://acm.ashland.edu/ http://www.acm.org/ Ignored http://acm.ashland.edu/ http://www.ibm.com/ http://acm.ashland.edu/ http://www.acm.org/ http://acm.ashland.edu/ http://www.ibm.com/ Ignored Source East Central North America 2001


水题一个。使用STL自带stack快速解决。有一个小细节需要注意:
BACK: Push the current page on the top of the forward stack. Pop the page from the top of the backward stack, making it the new current page. If the backward stack is empty, the command is ignored. FORWARD: Push the current page on the top of the backward stack. Pop the page from the top of the forward stack, making it the new current page. If the forward stack is empty, the command is ignored.
如果栈为空,则这个命令被忽略。被忽略的意思是就当没出现这个命令,自然也就不需要执行前半部分的操作了。
BACK: Push the current page on the top of the forward stack. FORWARD: Push the current page on the top of the backward stack.
完整代码如下: [cpp] #include<iostream> #include<stack> #include<string> using namespace std; int main() { //freopen("input.txt","r",stdin); string current_url="http://www.acm.org/"; stack<string> forward_url,backward_url; string command; while(cin>>command&&command!="QUIT") { if(command=="VISIT") { backward_url.push(current_url); cin>>current_url; cout<<current_url<<endl; while(!forward_url.empty()) { forward_url.pop(); } } else if(command=="BACK") { //forward_url.push(current_url);//If the backward stack is empty, the command is ignored忽略的话,这一句也不能执行 if(!backward_url.empty()) { forward_url.push(current_url); current_url=backward_url.top(); backward_url.pop(); cout<<current_url<<endl; } else { cout<<"Ignored"<<endl; } } else if(command=="FORWARD") { //backward_url.push(current_url); if(!forward_url.empty()) { backward_url.push(current_url); current_url=forward_url.top(); forward_url.pop(); cout<<current_url<<endl; } else { cout<<"Ignored"<<endl; } } } return 0; } [/cpp] 代码提交AC,用时0MS,内存200K。]]>

POJ 3278-Catch That Cow

POJ 3278-Catch That Cow Catch That Cow Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 48323 Accepted: 15139 Description Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,000) on a number line and the cow is at a point K (0 ≤ K ≤ 100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting. Walking: FJ can move from any point X to the points X – 1 or X + 1 in a single minute Teleporting: FJ can move from any point X to the point 2 × X in a single minute. If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it? Input Line 1: Two space-separated integers: N and K Output Line 1: The least amount of time, in minutes, it takes for Farmer John to catch the fugitive cow. Sample Input 5 17 Sample Output 4 Hint The fastest way for Farmer John to reach the fugitive cow is to move along the following path: 5-10-9-18-17, which takes 4 minutes. Source USACO 2007 Open Silver


又是一题广度搜索题,经过了上一题的锻炼,基本摸清了做广度优先搜索的套路,而且这一题比上一题还更简单一些。上一题是在三维空间里,而这一题只在一维直线上,只有三种情况:x-1、x+1、2*x。所以可以很快的写出代码: [cpp] #include<iostream> #include<queue> using namespace std; int n,k; int ans; int visited[100002]; const int MAX_DISTANCE=100000;//最大点位置 typedef struct P { int x;//坐标 int curr_min;//从n点到该点走过的时间 }; //广度优先搜索 void bfs() { queue<P> Q_P; P p,next_p; p.x=n; p.curr_min=0; visited[n]=1; Q_P.push(p); while(!Q_P.empty()) { p=Q_P.front(); Q_P.pop(); if(p.x==k) { ans=p.curr_min; return; } if(p.x-1>=0&&visited[p.x-1]==0) { visited[p.x-1]=1; next_p.x=p.x-1; next_p.curr_min=p.curr_min+1; Q_P.push(next_p); } if(p.x+1<=MAX_DISTANCE&&visited[p.x+1]==0) { visited[p.x+1]=1; next_p.x=p.x+1; next_p.curr_min=p.curr_min+1; Q_P.push(next_p); } if(2*p.x<=MAX_DISTANCE&&visited[2*p.x]==0) { visited[2*p.x]=1; next_p.x=2*p.x; next_p.curr_min=p.curr_min+1; Q_P.push(next_p); } } } int main() { cin>>n>>k; bfs(); cout<<ans; return 0; } [/cpp] 本代码提交AC,用时141MS,内存1304K。 仔细琢磨一下做过的深度搜索广度搜索的题,你会发现,一般要求最短距离、最优化问题的,都是用广度优先搜索BFS,比如这一题和上一题;如果只是求某一个可行方案,那么可以用深度搜索,比如POJ Problem 1321: 棋盘问题POJ Problem 1011: Sticks。当然广度搜索题其实也可以用深度搜索题解决,比如用深度搜索找出所有可行解,再求其中的最优解,但是这样的效率明显比只用广度搜索低。]]>

hihoCoder 1062-最近公共祖先·一

hihoCoder 1062-最近公共祖先·一 #1062 : 最近公共祖先·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho最近发现了一个神奇的网站!虽然还不够像58同城那样神奇,但这个网站仍然让小Ho乐在其中,但这是为什么呢? “为什么呢?”小Hi如是问道,在他的观察中小Ho已经沉迷这个网站一周之久了,甚至连他心爱的树玩具都弃置一边。 “嘿嘿,小Hi,你快过来看!”小Ho招呼道。 “你看,在这个对话框里输入我的名字,在另一个对话框里,输入你的名字,再点这个查询按钮,就可以查出来……什么!我们居然有同一个祖祖祖祖祖爷爷?” “诶,真是诶……这个网站有点厉害啊。”小Hi不由感叹道。 “是啊,这是什么算法啊,这么厉害!”小Ho也附和道。 “别2,我说的是他能弄到这些数据很厉害,而人类的繁殖树这种层数比较浅的树对这类算法的要求可是简单的不得了,你都能写出来呢!”小Hi道。 “啊?我也能写出来?可是……该从哪开始呢?”小Ho困惑了。 小Ho要面临的问题是这样的,假设现在他知道了N个人的信息——他们的父亲是谁,他需要对于小Hi的每一次提问——两个人的名字,告诉小Hi这两个人的是否存在同一个祖先,如果存在,那么他们的所有共同祖先中辈分最低的一个是谁? 提示:不着急,慢慢来,另外我有一个问题:挖掘机技术哪家强?! 输入 每个测试点(输入文件)有且仅有一组测试数据。 每组测试数据的第1行为一个整数N,意义如前文所述。 每组测试数据的第2~N+1行,每行分别描述一对父子关系,其中第i+1行为两个由大小写字母组成的字符串Father_i, Son_i,分别表示父亲的名字和儿子的名字。 每组测试数据的第N+2行为一个整数M,表示小Hi总共询问的次数。 每组测试数据的第N+3~N+M+2行,每行分别描述一个询问,其中第N+i+2行为两个由大小写字母组成的字符串Name1_i, Name2_i,分别表示小Hi询问中的两个名字。 对于100%的数据,满足N<=10^2,M<=10^2, 且数据中所有涉及的人物中不存在两个名字相同的人(即姓名唯一的确定了一个人)。 输出 对于每组测试数据,对于每个小Hi的询问,输出一行,表示查询的结果:如果根据已知信息,可以判定询问中的两个人存在共同的祖先,则输出他们的所有共同祖先中辈分最低的一个人的名字,否则输出-1。 样例输入 11 JiaYan JiaDaihua JiaDaihua JiaFu JiaDaihua JiaJing JiaJing JiaZhen JiaZhen JiaRong JiaYuan JiaDaishan JiaDaishan JiaShe JiaDaishan JiaZheng JiaShe JiaLian JiaZheng JiaZhu JiaZheng JiaBaoyu 3 JiaBaoyu JiaLian JiaBaoyu JiaZheng JiaBaoyu LinDaiyu 样例输出 JiaDaishan JiaZheng -1


最近公共祖先问题:给定一个家谱图,问某两个人的最近的公共祖先是谁,如果没有公共祖先,输出-1。最简单的方法题目提示得很清楚了:
当然你也先将一个人的祖先全都标记出来,然后顺着另一个的父亲一直向上找,直到找到第一个被标记过的结点,便是它们的最近公共祖先结点了。
所以我就简单粗暴的使用STL的MAP和SET来解决问题了。假设需要判断的两个人名分别为son1和son2,那么①首先把*son1*及其所有祖先都找到并加入到一个set中,然后②首先判断son2在不在前面的set中,③如果不在,再依次判断son2的祖先在不在set中。 这里需要注意几点: 1. son1的祖先包括son1本身,比如第二个样例中的JiaBaoyu和JiaZheng,JiaZheng的祖先就包括JiaZheng自己,所以需要上面的第①和②步。 2. 为什么使用set而不是vector呢?因为②③步需要在son1的祖先中搜索,set内部用红黑树实现,搜索的效率高于vector。 知道了以上几点,就可以很快的写出代码了,完整代码如下: [cpp] #include<iostream> #include<map> #include<string> #include <set> using namespace std; int main() { //freopen("input.txt","r",stdin); int n,t; cin>>n; string son,father; map<string,string> son_father;//map[son]=father for(int i=0;i<n;i++) { cin>>father>>son; son_father.insert(make_pair(son,father)); } cin>>t; string son1,son2,comm_father; while(t–) { comm_father=""; cin>>son1>>son2; //vector<string> grand_fathers; set<string> grand_fathers;//使用set的查找效率高于vector grand_fathers.insert(son1);//注意,son1,son2本身也是一个祖先,一定要加进去,否则错! while(son_father.find(son1)!=son_father.end()) { grand_fathers.insert(son_father[son1]); son1=son_father[son1]; } if(grand_fathers.find(son2)!=grand_fathers.end())//首先son2本身也是一个祖先,所以先判断一下//使用set的查找效率高于vector { if(t!=0) cout<<son2<<endl; else cout<<son2; continue; } while(son_father.find(son2)!=son_father.end()) { if(grand_fathers.find(son_father[son2])!=grand_fathers.end()) { comm_father=son_father[son2];//因为是son2从下往上找,所以第一个出下的肯定是最近的公共祖先 break; } else son2=son_father[son2]; } if(t!=0) { if(comm_father=="") cout<<"-1"<<endl; else cout<<comm_father<<endl; } else { if(comm_father=="") cout<<"-1"; else cout<<comm_father; } } return 0; } [/cpp] 本代码提交AC,用时4MS,内存0MB。 之前说了,我这种解法是非常简单粗暴的,事实上最近公共祖先问题是一个经典问题,有很多经典算法可以解决,我后面会进一步研究。]]>

POJ 2251-Dungeon Master

POJ 2251-Dungeon Master Dungeon Master Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 16959 Accepted: 6603 Description You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is composed of unit cubes which may or may not be filled with rock. It takes one minute to move one unit north, south, east, west, up or down. You cannot move diagonally and the maze is surrounded by solid rock on all sides. Is an escape possible? If yes, how long will it take? Input The input consists of a number of dungeons. Each dungeon description starts with a line containing three integers L, R and C (all limited to 30 in size). L is the number of levels making up the dungeon. R and C are the number of rows and columns making up the plan of each level. Then there will follow L blocks of R lines each containing C characters. Each character describes one cell of the dungeon. A cell full of rock is indicated by a ‘#’ and empty cells are represented by a ‘.’. Your starting position is indicated by ‘S’ and the exit by the letter ‘E’. There’s a single blank line after each level. Input is terminated by three zeroes for L, R and C. Output Each maze generates one line of output. If it is possible to reach the exit, print a line of the form Escaped in x minute(s). where x is replaced by the shortest time it takes to escape. If it is not possible to escape, print the line Trapped! Sample Input 3 4 5 S…. .###. .##.. ###.# ##### ##### ##.## ##… ##### ##### #.### ####E 1 3 3 S## #E# ### 0 0 0 Sample Output Escaped in 11 minute(s). Trapped! Source Ulm Local 1997


经典广度优先搜索题,网上居然把这题分到深度优先搜索了,害的我写好DFS提交后得到个TLE。 首先分析题意,给出一个立体的迷宫,要求从S走到E,求最短距离,其中迷宫中#不能走,.这个能走。我们可以用三维数组char dungeon[MAX_SIZE][MAX_SIZE][MAX_SIZE];来存储迷宫,对于某一个点dungeon[i][j][k],可以试探下面六个点:dungeon[i-1][j][k]、dungeon[i+1][j][k]、dungeon[i][j-1][k]、dungeon[i][j+1][k]、dungeon[i][j][k-1]、dungeon[i][j][k+1],想象你站在空间某一点,你可以往上、下、前、后、左、右走。 如果是深度搜索,试探到某点可行之后,访问它,并以该点为基点继续试探下去,直到到达E点或者无路可走,然后清除访问,每次到达E点,就比较一下这次走过的距离和上次的距离哪个小,当所有点都试探完毕时,就能得到最小值。 如果是广度搜索,以S点为起点,找到所有可行的下一个点,访问他们,并把他们插入到一个队列,然后在队列不为空的情况下循环:取出队首元素,以该元素为基点,找到所有可行的下一个点,访问他们,并把他们加入到队列中。如果广度搜索的过程中遇到E点,则此时走过的距离就是最小值,因为是广度搜索,最先访问到的肯定是最小值,不需要像深度搜索那样再进行比较。 下面是完整代码: [cpp] #include<iostream> #include<queue> using namespace std; const int MAX_SIZE=60; const int MAX_DISTANCE=1024; int L,R,C; char dungeon[MAX_SIZE][MAX_SIZE][MAX_SIZE];//迷宫 //int visited[MAX_SIZE][MAX_SIZE][MAX_SIZE]; int ans; int s_l,s_r,s_c,e_l,e_r,e_c; typedef struct P//点 { int x,y,z;//坐标 int dist;//从S到该点走过的距离 }; //深度搜索,结果正确,但是TLE /*void dfs(int l,int r,int c,int steps) { int visited[MAX_SIZE][MAX_SIZE][MAX_SIZE]={0};//每次初始化 if(l==e_l&&r==e_r&&c==e_c)//搜索成功 { if(steps<ans)//求最小值 ans=steps; return; } //深度搜索的六种情况 if((l-1)>=0&&visited[l-1][r][c]==0&&dungeon[l-1][r][c]==’.’) { visited[l-1][r][c]=1; dfs(l-1,r,c,steps+1);//继续深度搜索 visited[l-1][r][c]=0;//回溯 } if((l+1)<L&&visited[l+1][r][c]==0&&dungeon[l+1][r][c]==’.’) { visited[l+1][r][c]=1; dfs(l+1,r,c,steps+1); visited[l+1][r][c]=0; } if((r-1)>=0&&visited[l][r-1][c]==0&&dungeon[l][r-1][c]==’.’) { visited[l][r-1][c]=1; dfs(l,r-1,c,steps+1); visited[l][r-1][c]=0; } if((r+1)<R&&visited[l][r+1][c]==0&&dungeon[l][r+1][c]==’.’) { visited[l][r+1][c]=1; dfs(l,r+1,c,steps+1); visited[l][r+1][c]=0; } if((c-1)>=0&&visited[l][r][c-1]==0&&dungeon[l][r][c-1]==’.’) { visited[l][r][c-1]=1; dfs(l,r,c-1,steps+1); visited[l][r][c-1]=0; } if((c+1)<C&&visited[l][r][c+1]==0&&dungeon[l][r][c+1]==’.’) { visited[l][r][c+1]=1; dfs(l,r,c+1,steps+1); visited[l][r][c+1]=0; } }*/ //广度搜索 bool bfs(int s_l,int s_r,int s_c) { int visited[MAX_SIZE][MAX_SIZE][MAX_SIZE]={0};//记得每次都要初始化为0,否则会影响下一个case queue<P> P_Q;//队列 P p,next_p; p.x=s_l,p.y=s_r,p.z=s_c,p.dist=0; visited[s_l][s_r][s_c]=1; P_Q.push(p); int l,r,c; while(!P_Q.empty()) { p=P_Q.front(); P_Q.pop(); if(p.x==e_l&&p.y==e_r&&p.z==e_c) { ans=p.dist;//因为是广度搜索,最先到达E,肯定就是最小值 return true; } l=p.x,r=p.y,c=p.z; //广度搜索的六种情况 if((l-1)>=0&&visited[l-1][r][c]==0&&dungeon[l-1][r][c]==’.’) { visited[l-1][r][c]=1; next_p.x=l-1; next_p.y=r; next_p.z=c; next_p.dist=p.dist+1; P_Q.push(next_p); } if((l+1)<L&&visited[l+1][r][c]==0&&dungeon[l+1][r][c]==’.’) { visited[l+1][r][c]=1; next_p.x=l+1; next_p.y=r; next_p.z=c; next_p.dist=p.dist+1; P_Q.push(next_p); } if((r-1)>=0&&visited[l][r-1][c]==0&&dungeon[l][r-1][c]==’.’) { visited[l][r-1][c]=1; next_p.x=l; next_p.y=r-1; next_p.z=c; next_p.dist=p.dist+1; P_Q.push(next_p); } if((r+1)<R&&visited[l][r+1][c]==0&&dungeon[l][r+1][c]==’.’) { visited[l][r+1][c]=1; next_p.x=l; next_p.y=r+1; next_p.z=c; next_p.dist=p.dist+1; P_Q.push(next_p); } if((c-1)>=0&&visited[l][r][c-1]==0&&dungeon[l][r][c-1]==’.’) { visited[l][r][c-1]=1; next_p.x=l; next_p.y=r; next_p.z=c-1; next_p.dist=p.dist+1; P_Q.push(next_p); } if((c+1)<C&&visited[l][r][c+1]==0&&dungeon[l][r][c+1]==’.’) { visited[l][r][c+1]=1; next_p.x=l; next_p.y=r; next_p.z=c+1; next_p.dist=p.dist+1; P_Q.push(next_p); } } return false; } int main() { //freopen("input.txt","r",stdin); while(cin>>L>>R>>C&&L&&R&&C) { ans=MAX_DISTANCE; for(int i=0;i<L;i++) for(int j=0;j<R;j++) for(int k=0;k<C;k++) { cin>>dungeon[i][j][k]; if(dungeon[i][j][k]==’S’) { s_l=i; s_r=j; s_c=k; } else if(dungeon[i][j][k]==’E’) { dungeon[i][j][k]=’.’;//注意这里改成了’.’,便于BFS里搜索判断 e_l=i; e_r=j; e_c=k; } } //dfs(s_l,s_r,s_c,0); if(bfs(s_l,s_r,s_c)) cout<<"Escaped in "<<ans<<" minute(s)."<<endl; else cout<<"Trapped!"<<endl; } return 0; } [/cpp] 本代码提交AC,用时63MS,内存1248K。]]>

POJ 1125-Stockbroker Grapevine

POJ 1125-Stockbroker Grapevine Stockbroker Grapevine Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 27559 Accepted: 15273 Description Stockbrokers are known to overreact to rumours. You have been contracted to develop a method of spreading disinformation amongst the stockbrokers to give your employer the tactical edge in the stock market. For maximum effect, you have to spread the rumours in the fastest possible way. Unfortunately for you, stockbrokers only trust information coming from their “Trusted sources” This means you have to take into account the structure of their contacts when starting a rumour. It takes a certain amount of time for a specific stockbroker to pass the rumour on to each of his colleagues. Your task will be to write a program that tells you which stockbroker to choose as your starting point for the rumour, as well as the time it will take for the rumour to spread throughout the stockbroker community. This duration is measured as the time needed for the last person to receive the information. Input Your program will input data for different sets of stockbrokers. Each set starts with a line with the number of stockbrokers. Following this is a line for each stockbroker which contains the number of people who they have contact with, who these people are, and the time taken for them to pass the message to each person. The format of each stockbroker line is as follows: The line starts with the number of contacts (n), followed by n pairs of integers, one pair for each contact. Each pair lists first a number referring to the contact (e.g. a ‘1’ means person number one in the set), followed by the time in minutes taken to pass a message to that person. There are no special punctuation symbols or spacing rules. Each person is numbered 1 through to the number of stockbrokers. The time taken to pass the message on will be between 1 and 10 minutes (inclusive), and the number of contacts will range between 0 and one less than the number of stockbrokers. The number of stockbrokers will range from 1 to 100. The input is terminated by a set of stockbrokers containing 0 (zero) people. Output For each set of data, your program must output a single line containing the person who results in the fastest message transmission, and how long before the last person will receive any given message after you give it to this person, measured in integer minutes. It is possible that your program will receive a network of connections that excludes some persons, i.e. some people may be unreachable. If your program detects such a broken network, simply output the message “disjoint”. Note that the time taken to pass the message from person A to person B is not necessarily the same as the time taken to pass it from B to A, if such transmission is possible at all. Sample Input 3 2 2 4 3 5 2 1 2 3 6 2 1 2 2 2 5 3 4 4 2 8 5 3 1 5 8 4 1 6 4 10 2 7 5 2 2 2 5 1 5 Sample Output 3 2 3 10 Source Southern African 2001


首先读懂题意。Stockbroker想要给每一个人传播谣言,问从哪一个人开始传播,可以使谣言传播得最快,并求最快的传播时间是多少。转换成数据结构的语言就是:给你一张图,问从哪一点到其他所有点的最短距离中的最长者最短。表述起来有点绕,下面用图示来说明。 假设分别从A点和B点开始的单源最短距离如上两张图所示,那么Stockbroker从A点开始传播谣言总共需要多长时间呢?因为传播是可以同时进行的,所以传播结束时间就是A点到其他点中的最长距离,也就是上图中的AC。同理,如果从B点开始,则|BD|是传播时间。 综上所述,本题的求解过程可以归纳为以下几步: 1. 测试图中的每一个点$$i$$,求从该点出发,到其他所有点的单源最短距离集合$$S_i$$ 2. 求出该单源最短距离$$S_i$$内部的最大值$$M_i$$ 3. 对于图中的所有点,求其$$M_i$$ 4. 最后求所有$$M_i$$中的最小值$$MIN$$,即为最终结果 所以根据上面的步骤,我们可以用n次Dijkstra算法求每个点的单源最短距离内部的最大值$$M_i$$,然后求$$MIN$$。其实也可以不用这么麻烦,因为我们还有另外一个更简单的算法Floyd算法。使用Floyd算法求得所有点对的最短距离,自然就知道某个点的单源最短距离了。 在写代码的时候还有一些小问题需要注意,对于数据输入,请仔细多读几遍Input说明,如果还是有困难,请看这里,题目还要求如果是非连通图,输出disjoint,那么怎么来判断是否是非连通图呢?我使用的方法是:把图中不相连的两条边的距离设置为MAX_DISTANCE=1024,因为本题的距离取值范围是[1,10],所以如果最终求出来的最小值大于MAX_DISTANCE,说明在求解的时候通过了不相连的边,也就说明这个图是非连通图。(后来我仔细想了一下,这个方法好像不对,但是代码还是AC,据说数据比较弱。) 完整代码如下: [cpp] #include<iostream> using namespace std; const int MAX_DISTANCE=1024;//距离取值范围[1,10],所以用1024代表无穷大 const int MAX_PEOPLE_NUM=101;//最多传播者 int path[MAX_PEOPLE_NUM][MAX_PEOPLE_NUM];//路径 int n;//实际人数 //初始化路径都为0 void init_path() { for(int i=0;i<n;i++) for(int j=0;j<n;j++) path[i][j]=0; } //floyd算法求所有点对的最短距离 void floyd() { for(int k=0;k<n;k++) { for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { if(path[i][j]>path[i][k]+path[k][j]) path[i][j]=path[i][k]+path[k][j]; } } } } int main() { int connected_num,person_index; while(cin>>n&&n) { init_path(); //输入数据 for(int i=0;i<n;i++) { cin>>connected_num; for(int j=0;j<connected_num;j++) { cin>>person_index; cin>>path[i][person_index-1]; } } //给不相连的两者的距离赋为最大值无穷 for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { if(i!=j&&path[i][j]==0) path[i][j]=MAX_DISTANCE; } } floyd();//调用floyd算法,求得所有点对的最短距离,还是保存在path[][]中 int longest_path,global_shorest=MAX_DISTANCE,start_person; for(int i=0;i<n;i++)//对于以i为起点的单源最短距离 { longest_path=0;//求出这个单源最短距离中的最大值 for(int j=0;j<n;j++) if(path[i][j]>longest_path) longest_path=path[i][j]; if(longest_path<global_shorest)//再求其最小值 { start_person=i; global_shorest=longest_path; } } if(global_shorest<MAX_DISTANCE) cout<<start_person+1<<" "<<global_shorest<<endl; else cout<<"disjoint"<<endl;//非连通图 } return 0; } [/cpp] 本代码提交AC,用时0MS,内存256K。网上有人讨论还可以用SPFA算法,改天学习了重新提交一个版本。 ]]>