题目描述
奶牛 Bessie 正准备从她最喜爱的草地回到她的牛棚。
农场位于一个 N×NN \times NN×N 的方阵上(2≤N≤502 \leq N \leq 502≤N≤50),其中她的草地在左上角,牛棚在右下角。Bessie 想要尽快回家,所以她只会向下或向右走。有些地方有草堆(haybale),Bessie 无法穿过;她必须绕过它们。
Bessie 今天感到有些疲倦,所以她希望改变她的行走方向至多 KKK 次(1≤K≤31 \leq K \leq 31≤K≤3)。
Bessie 有多少条不同的从她最爱的草地回到牛棚的路线?如果一条路线中 Bessie 经过了某个方格而另一条路线中没有,则认为这两条路线不同。
分析
搜索+剪枝……
一个十分暴力的搜索是:记录转弯次数 cntcntcnt,和目前方向 pospospos,每次搜索是判断方向是否与 pospospos 相同,如果不同则 cntcntcnt 要加 111,但是我们会发现除了样例这个程序其他点都过不了,所以考虑剪枝:
- 如果走到点 (x,y)(x,y)(x,y) 时,转弯次数已经大于 kkk,就进行剪枝。
- 如果走到点 (x,y)(x,y)(x,y) 时,转弯次数已经等于 kkk,但是 x,yx,yx,y 都不等于 nnn,则此时至少还需一次转弯,进行剪枝。
- 我们发现搜索树里有很多重复状态,所以考虑记忆化搜索,设 dpx,y,cnt,nowdp_{x,y,cnt,now}dpx,y,cnt,now 表示走到点 (x,y)(x,y)(x,y) 已经转了 cntcntcnt 次弯,当前方向是 nownownow。
代码
这里给出不用记忆化搜索的代码。
#include <bits/stdc++.h>
#define debug puts("Y")
#define int long long
using namespace std;
const int N = 55;
char c[N][N];
int n, k, cnt;
int dx[] = {0, 1}, dy[] = {1, 0};//0:右 1:下
void dfs(int x, int y, int cir, int pre){
if(c[x][y] == 'H' || cir > k + 1 || (x != n && y != n && cir == k + 1)){//由于第一次搜索时不好处理,所以直接将转弯次数看成k+1即可
return ;
}
if(x == n && y == n){
cnt ++;
return ;
}
for(int i = 0; i < 2; i ++){
int nx = x + dx[i], ny = y + dy[i];
if(i != pre){
dfs(nx, ny, cir + 1, i);
}else{
dfs(nx, ny, cir, i);
}
}
}
void solve(){
cnt = 0;
cin >> n >> k;
memset(c, 'H', sizeof c);//在外面包一圈障碍物,这样就不用判越界了
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= n; j ++){
cin >> c[i][j];
}
}
dfs(1, 1, 0, 114514);
cout << cnt << "\n";
}
signed main(){
int T;
for(cin >> T; T; T --){
solve();
}
return 0;
}
/*
*/