題目鏈接: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