题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5335
五个小时就A了这一题,一个小细节检查了两个小时,服的,谁叫自己考虑不周呢。
题意:
一个闲的没事的人走迷宫,从左上角走到右下角,问怎么走组成的二进制数最小?
思路:
没啥思路,起点如果是0的话DFS找一遍0的路径,看看离终点最近的那个0点在那个斜线上(因为可能有多个0点在一个斜线上),它们离终点的距离最短,且前缀都是0,组成的二进制位数一定最少,数字就一定最小。
找到后开始一个个dp,因为此时位数已经确定了。记得以前做过一题是问从起点走到终点咋样花费最少,肯定只往离终点方向更近,且和最小的地方走啊,这道题只不过把花费int,改成了string(int存不下)。如果起点不是0,不好意思直接DP。
贴心的我配了副图,哈哈:
//箭头是斜线
AC代码:
//没优化的,感觉找斜线上的点可以更快点,比如用个结构体保存,但怕超内存。说起内存dp数组最好开一维的,反正是滚动更新。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std ;
int n,m,mindis;
const int N = 1005;
#define INF 0x3f3f3f3f
int val[N][N]; //DFS用的标记
string dp[N],ans; //Dp数组,ans答案
char a[N][N]; //迷宫
int dxy[4][2] = {0,1,1,0,0,-1,-1,0}; //方向
bool DFSwalk( int x,int y ) //DFS可走区域
{
if( x<0 || y<0 || x>=n || y>=m || val[x][y] )
return false;
return true;
}
void DFS( int x,int y )
{
mindis = min( mindis , n-x+m-y-2 ); //mindis用于记录该点到终点的最少距离,可理解为位数大小,-2因为图保存在[0,n-1]
val[x][y] = 1;
int tx,ty;
for( int i=0; i<4; i++ )
{
tx = x +dxy[i][0];
ty = y +dxy[i][1];
if( DFSwalk(tx,ty) && a[tx][ty]=='0' )
{
DFS(tx,ty);
}
}
}
void DP( int x,int y ) //一个一个区间DP
{
for( int i=x; i<n; i++ )
{
for( int j=y; j<m; j++ )
{
if( i==x && j!=y )
{
dp[j] = dp[j-1] + a[i][j];
}
else if( j==y && i!=x )
{
dp[j] = dp[j] + a[i][j];
}
else if( i!=x && j!=y )
{
dp[j] = min( dp[j],dp[j-1] )+ a[i][j]; //min函数贼好用
}
}
}
if( ans=="x" ) //这里因为一开始没有想到这么办ans赋最大值与其他string比较,所以多写一步
ans = dp[m-1];
else
ans = min( ans,dp[m-1] );
}
int main()
{
int T;
scanf("%d",&T);
while( T-- )
{
scanf("%d%d",&n,&m);
mindis = n+m-2 ;
ans = "x";
memset(val,0,sizeof(val));
for(int i=0; i<=m; i++ )
dp[i] = ""; //string[]清空
for( int i=0; i<n;i++ )
scanf("%s",a[i]);
if(a[0][0]=='0')
DFS(0,0);
if(!mindis&&a[0][0]=='0') //开头0直接到达终点0,直接输出0,两个条件必须都要,否则嘻嘻,WA等着你
{
printf("0\n");
continue;
}
else if( mindis == n+m-2 && a[0][0]=='1') //开头1且mindis就在a[0][0],开头dp,这步可省略,写出来是为了节省时间
{
dp[0] = a[0][0];
DP(0,0);
}
else //下面的情况就按我说的来~~
{
for( int i=0; i<n; i++ )
{
for( int j=0; j<m; j++ )
{
if( val[i][j] && n+m-2==i+j+mindis )
{
DP(i,j);
}
}
}
}
cout << ans << endl ;
}
return 0;
}
测试数据:
13
5 5
00011
11111
00000
11111
00011
5 5
00000
00000
00000
00001
00010
6 6
000000
000000
000000
000000
000001
000011
6 6
100000
000001
010101
100001
111111
110111
8 8
10010010
00101001
10101100
11000101
11001101
10001001
11100011
01010010
5 5
00000
11110
00000
01111
00000
3 3
101
110
111
5 5
00100
00100
00100
00100
01101
5 5
01011
00000
10001
01010
11111
1 1
0
1 1
1
1 2
01
2 1
0
1
1 2
00
2 1
0
0
答案:
100011
10
11
10000000111
100010000000010
0
10101
1001
101
0
1
1
1
0
0