您的位置:首页 > 其它

CSU1119/UVA 12510 Collecting Coins

2016-08-14 23:43 232 查看
CSU1119/UVA 12510 Collecting Coins

Time Limit:3000MS     MemoryLimit:131072KB     64bit IO Format:%lld& %llu
Submit Status Practice CSU
1119
Description
In a maze of rrows and c columns, your task is to collect as many coins as possible.
Each square iseither your start point "S"(which will become empty after you leave),an empty square ".", a coin square "C" (which will becomeempty after you step on this square and
thus collecting the coin), a rocksquare "O" or an obstacle square "X".
At each step,you can move one square to the up, down, left or right. You cannot leave themaze or enter an obstacle square, but you can push each rock at most once (i.e.You can treat
a rock as an obstacle square after you push it).
To push a rock,you must stand next to it. You can only push the rock along the directionyou're facing, into an neighboring empty square (you can't push it outside themaze, and you
can't push it to a squarecontiaining a coin).For example, if therock is to your immediate right, you can only push it to its right neighboringsquare.
Find the maximalnumber of coins you can collect.
Input
The first lineof input contains a single integer T (T<=25), the number of testcases. 
Each test casebegins with two integers r and c (2<=r,c<=10), then followed by r lines,each with c columns. 
There will be atmost 5 rocks and at most 10 coins in each maze.
Output
For each testcase, print the maximal number of coins you can collect.
Sample Input
3
3 4
S.OC
..O.
.XCX
4 6
S.X.CC
..XOCC
...O.C
....XC
4 4
.SXC
OO.C
..XX
.CCC
Sample Output
1
6
3
 

题意:给定一个图,图中有金币,石头和障碍物,从S出发,石头可以推一次,而且只能推一次,只能退到一个空的地方,s点离开时变为空,某点金币得到之后该点也为空,求能得到的最大金币数。

 

这个题目是个很明显的搜索题,直接DFS枚举深搜可以得到答案,但是问题是会超时,不能直接深搜,解题的方法还是很特别的,首先在搜索之前存储石头的位置,然后不推石头搜索一次求得能够得到的金币数,这样的话求得了能够得到的金币数,然后再从石头出发,对石头进行一次搜索,把石头看做点进行搜索求得不同的推石头的顺序所能得到的最大值,具体步骤是:如果一个石头周围的四个点中有一个点被标记且该点对面的点为空的话证明此处的石头可以推,然后从该点开始进行DFS搜索并且标记走过的点一直递归进行搜索,之后再次去进行推石头的搜索,每次回溯之后把之前推石头走过的点进行复位,然后重新去进行不同的顺序推搜索,既然这样,在一次推石头的搜索过程中每次推石头时的点需要进行不同的标记以区别与推之前的石头时所标记的点,这样才好进行之后的恢复,这样搜索求得推石头能够得到的最大金币数目最后和之前不推石头的相加最后得到最大的金币数目。

 

具体代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
#define LL long long

using namespace std;
char maze[15][15];
int vis[15][15];
int k, r, c, sx, sy, t, m;
int sum, res, ans, maxc;
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
struct po
{
int x, y, vis;
po(int a, int b, int c):x(a), y(b), vis(c){}
po(){}
}ro[10];

int check(int x, int y)
{
return (x<0 || x>=r || y<0 || y>=c);
}

void dfs(int x, int y, int tag){
vis[x][y] = tag;
for (int i = 0; i < 4; i++){
int nx = x+dx[i], ny=y+dy[i];
if (check(nx, ny) || vis[nx][ny] || maze[nx][ny]=='O'|| maze[nx][ny]=='X') continue;
if(maze[nx][ny] == 'C') res++;
dfs(nx, ny, tag);
}
}

void back(int x, int y, int tag)
{
vis[x][y] = 0;
for (int i = 0; i < 4; i++){
int nx = x+dx[i], ny=y+dy[i];
if (check(nx, ny) || vis[nx][ny]!=tag) continue;
back(nx, ny, tag);
}
}

void solve(int num)
{
if (num>=k || ans+sum==m) return;
for (int i = 0; i<k && ans+sum<m; i++){
if (ro[i].vis) continue;
ro[i].vis = 1;
int x = ro[i].x, y = ro[i].y;
for (int j = 0; j < 4; j++){
int nx = x+dx[j], ny = y+dy[j];
if (check(nx, ny) || maze[nx][ny]=='X' || !vis[nx][ny]) continue;
nx = x-dx[j], ny = y-dy[j];
if (check(nx, ny) || maze[nx][ny]!='.') continue;
maze[nx][ny] = 'X', maze[x][y] = '.';
res = 0;
dfs(x, y, i+3);
maxc += res;
int t = res;
solve(num+1);
ans = max(ans, maxc);//与一次推石头的搜索能得到的最大数组进行比较取得最大值
back(x, y, i+2);;//回溯后复位
maxc -= t;//在一次推石头的搜索返回后减去最后推石头能得到的金币数目,也就是回溯到上一步重新进行搜索
maze[nx][ny] = '.', maze[x][y] = 'O';
}
ro[i].vis = 0;
}
}

int main()
{
scanf("%d", &t);
while (t--){
m = k = 0;
scanf("%d %d", &r, &c);
for (int i = 0; i < r; i++){
scanf("%s", maze[i]);
for (int j = 0; j < c; j++){
if (maze[i][j] == 'S') sx = i, sy = j, maze[i][j]='.';
if (maze[i][j] == 'O') ro[k++] = po(i, j, 0);
if (maze[i][j] == 'C') m++;
}
}
memset(vis, 0, sizeof(vis));
maxc = ans = res = 0;
dfs(sx, sy, 1);
sum = res;
solve(0);
printf("%d\n", ans+sum);
}
return 0;
}


 

总结:

这个题目还是不错的,很少遇到过这种深搜超时但又不能剪枝优化时间复杂度。重新审视这个题目,如果直接深搜,考虑到每一次状态转移可能是金币,空点,石头,从最开始的S点出发,每次再在一个点,首先从某一个方向进行搜索,之后回溯之后从四个方向的其他方向搜索,这样,可能的情况将是4的n次方指数级增长的状态搜索次数,加上遇到石头的情况简直无法想象。。。。,总之直接深搜需要搜索的次数和不同的状态转移状态数目将是非常巨大的,我尝试过了构建一个10*10的地图然后设置大量的空点,运行下程序发现出不来结果,像死循环一样了…..其实仔细想想会发现中间搜索的过程有很多重复,所以首先不推石头把能走的点都走一遍求得金币数目最后推石头求得能够得到的金币数目相加就可以了。

 

搜索虽说是最基础的算法,它的结果明显是绝对正确的,几乎大部分题目都可以枚举搜索解决,但是存在的问题是效率问题,所以使用搜索还得灵活运用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM学习 搜索 题解 dfs