题意:在一个R行C列 (2≤R,C≤15,R∗C≤30) 的矩阵里有障碍物和数字格(包含1~9的数字)。你可以从任意一个数字格出发,每次沿着上下左右之一的方向走一格,但不能走到障碍格中,也不能重复经过一个数字格,然后把沿途经过的所有数字连起来,如图所示。如图可以得到9784,4832145等整数。问:能得到的最大整数是多少?(本段摘自《算法竞赛入门经典(第2版)》)
分析:
枚举起点进行DFS即可。有一个最优性剪枝,即当当前位置的时候,剩下可以走的最大长度加上已走长度如果仍然小于当前最优的答案时,直接return,如果相等但是字典序比当前最优答案小的话也直接return。
#include<bits/stdc++.h>
using namespace std;
char s[22][22];
int vis[22][22],vis1[22][22],a[33];
int dx[]={-1,0,1,0};
int dy[]={0,1,0,-1};
int n,m;
struct P
{
int x,y;
P(int x,int y):x(x),y(y){}
P(){}
};
string ans;
int bfs(int x,int y)
{
int i,j,tx,ty,len=0;
queue<P>que;
que.push(P(x,y));
vis1[x][y]=1;
while(!que.empty()) {
P p = que.front();
que.pop();
for(i=0;i<4;i++) {
tx=p.x+dx[i];
ty=p.y+dy[i];
if(tx<0||tx>=n||ty<0||ty>=m||vis[tx][ty]||vis1[tx][ty]||s[tx][ty]=='#') continue;
vis1[tx][ty]=1;
a[len++]=s[tx][ty]-'0';
que.push(P(tx,ty));
}
}
return len;
}
void dfs(int x,int y,string tmp)
{
int i,j,tx,ty;
// cout<<tmp<<endl;
/ 剪枝
if(tmp.length()>ans.length()||(tmp.length()==ans.length() && tmp>ans)) ans=tmp;
else {
memset(vis1,0,sizeof(vis1));
int len = bfs(x,y);
if(ans.length()>tmp.length()+len) return;
sort(a,a+len,greater<int>());
string t = tmp;
for(i=0;i<len;i++) {
t+=char(a[i]+'0');
}
if(ans.length()==t.length() && ans>t) return ;
}
剪枝
for(i=0;i<4;i++) {
tx=x+dx[i],ty=y+dy[i];
if(tx<0||tx>=n||ty<0||ty>=m||vis[tx][ty]||s[tx][ty]=='#') continue;
vis[tx][ty] = 1;
dfs(tx,ty,tmp+s[tx][ty]);
vis[tx][ty]=0;
}
}
int main()
{
int i,j;
string tmp;
ios::sync_with_stdio(false);
while(cin>>n>>m) {
if(n==0 && m==0) break;
for(i=0;i<n;i++) cin>>s[i];
ans="";
for(i=0;i<n;i++) {
for(j=0;j<m;j++) {
tmp="";
if(s[i][j]=='#') continue;
vis[i][j]=1;
dfs(i,j,tmp+s[i][j]);
vis[i][j]=0;
}
}
cout<<ans<<endl;
}
return 0;
}