湖南省第六届大学生程序设计大赛原题 F Biggest Number (UVA1182)
Biggest Number
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=30851#problem/F
解题思路:DFS(检索)+BFS(探路)=强剪枝
一看就知道是搜索题,只是,要是DFS裸搜,真的会超时。时间,非常紧!
做该题的时候,要无时无刻不忘剪枝,抓住一切剪枝的机会,剪吧!
(1)用flag记录当前检索的值与当前已经检索到的最大值的关系,一但有机会,就要剪掉那些检索到的值会比当前最大值要小的“残枝败叶”。
(2)每次DFS检索之前,一定要用BFS“探路”,要是该次检索的值的最大长度小于当前最大值的最大长度,赶紧剪掉。这次搜索到的值不可能比当前最大值大,搜了也白搜,赶紧结束,节约时间。
这里我给出两个代码,一个是参考别人的代码注释来得,一种是用自己的想法从写得到的。
首先,我承认自己做不出来,一直超时。但我是个锲而不舍的孩子,在网上查了资料,弄懂了。代码主人,只在博客中给出代码,没有思路描绘,也没有注释。我做了注释后的成果如下:
1532762 | 20114045007 | F | Accepted | 0 KB | 329 ms | C++ 4.5.3 | 4881 B | 2013-09-03 20:55:11 |
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
int x,y;
} queue[1000];
int n,m;
int Max,flag,tatal; //当前检索到的最大值的长度、当前检索值首数字与当前检索到的最大值的首数字的关系、数字总数
char map[20][20]; //输入地图存储
char ans[35],stack[35]; //当前检索到的最大值、当前检索值的情况
int dir[4][2]= {-1,0,0,-1,0,1,1,0}; //四个方向
bool yes(int x,int y)
{
return x>=0&&x<n&&y>=0&&y<m; //该点是否在地图中,是就返回true
}
int bfs(int x,int y) //(x,y)处往后还能搜索到多少位有效数字
{
node t;
char g[20][20];
for(int i=0; i<n; i++)strcpy(g[i],map[i]); //将g中的值赋为map
int head,tail; //队列前后”指针“
head=tail=0; //队列为空
t.x=x,t.y=y; //其实节点,用于入队
queue[tail++]=t; //数组模拟队列
while(head<tail) //队列不为空
{
x=queue[head].x;
y=queue[head++].y; //取出队头元素,队头后移一位
for(int i=0; i<4; i++) //四面查找
{
int xx=x+dir[i][0];
int yy=y+dir[i][1];
if(!yes(xx,yy)||g[xx][yy]=='#')continue; //该点不在地图中,该点不可处理【不处理该点】
g[xx][yy]='#'; //要处理该点,标记,预防重处理
t.x=xx,t.y=yy; //记录情况
queue[tail++]=t; //新节点入“队”
}
}
return head; //该次处理的点数
}
void dfs(int x,int y,int cnt) //当前检索点的坐标情况,当前检索到的数的长度
{
if(Max<cnt||(Max==cnt&&flag==1)) //当前检索到的数的长度大于当前检索到的最大的数的总长度“或”长度相等但是当前值首数字比较大
//当前检索到的值必定大于当前检索到的“最大值”【强剪枝2】
{
stack[cnt]=0; //字符串结束标志【别忘了,字符数组没有结束标志会输出错误的】
strcpy(ans,stack); //将当前值赋给”最大值“
Max=cnt; //改变最大值的长度
flag=0; //大小不确定
}
int res=bfs(x,y); //该点后面还能搜索到res个有效数字
if(res+cnt-1<Max||(res+cnt-1==Max&&flag==-1))return; //若该点参与的检索中的有效数字的最大长度小于当前检索到的数字中的最大值的长度
//长度相等,但是该次检索的数字要小【这个数肯定小于当前检索到的最大值】直接结束搜索【强剪枝3】
for(int i=0; i<4; i++) //当前的搜索得到的数肯定会大于当前检索到的最大值,【搜索继续】
{
int xx=x+dir[i][0];
int yy=y+dir[i][1];
if(!yes(xx,yy)||map[xx][yy]=='#')continue; //该点不在地图中,或该点不可处理【不作处理】【常规剪枝】
if(flag!=1&&ans[cnt]>map[xx][yy]&&tatal==Max)continue; //当前检索的值不可能大于当前检索到的最大值【强剪枝4】
stack[cnt]=map[xx][yy]; //检索到的数字可用,加入到临时【当前检索值的情况】中
map[xx][yy]='#'; //记得标记哦,检索过的就不要再检索了
if(flag==0) //【】【】大小不确定
{
if(cnt>=Max)flag=1; //长度,当前检索到的比较大
else if(ans[cnt]==stack[cnt])flag=0; //长度想相同就比首字母 【大小不确定】
else if(ans[cnt]<stack[cnt])flag=1; //当前检索到的i较大
else flag=-1; //当前检索到的比较小
dfs(xx,yy,cnt+1); //长度加1
flag=0;//【大小不确定】
}
else dfs(xx,yy,cnt+1); //若大小确定,继续检索
map[xx][yy]=stack[cnt]; //搜索归来,抹去标记
}
}
int main()
{
while(scanf("%d%d",&n,&m)&&n+m)
{
tatal=0;
for(int i=0; i<n; i++)
{
scanf("%s",map+i);
for(int j=0; j<m; j++)
{
if(map[i][j]!='#')tatal++;
}
}
Max=1;
memset(ans,0,sizeof(ans));
for(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
{
if(map[i][j]=='#')continue;
if(Max==tatal&&ans[0]>map[i][j])continue; //当前检索到的最大值,长度已达到最长&&开头数字比当前检索数字大
//以当前数字开头检索到的最大值肯定小于当前检索到的最大值【强剪枝1】
stack[0]=map[i][j];
map[i][j]='#'; //访问标记
if(ans[0]==stack[0])flag=0;//大小不确定
else if(ans[0]<stack[0])flag=1; //大于
else flag=-1; //小于
dfs(i,j,1);
map[i][j]=stack[0]; //搜索归来,抹掉标记
}
}
printf("%s\n",ans); //输出最终答案
}
return 0;
}
原码转自:http://blog.csdn.net/andring/article/details/7376129
本人代码:更新中。。。