北理2017校赛 - C.lv棋 - dfs+剪枝

25 篇文章 0 订阅
4 篇文章 0 订阅

1.题目描述:

lv棋

时间限制 7s

 

 

在集训队中非常流行一种lv棋,这种棋的棋盘是一个n乘m的网格,双方分为进攻方和防守方。

防守方在棋盘上三个点摆下三个棋子(这三个棋子有先后顺序,摆下后位置不能改变),

进攻方需要选择一条路径从起点(0,0)走到终点(0,1),每次只能向只能上、下、左、右方向移动,且只能移动一个单位。进攻方经过每一个格子一次且仅一次,且依次吃掉三个防守方棋子最后走到终点才算完成一次“进攻”。

强神和森神现在打算玩这个游戏,森神摆放完防守棋子后,强神说我A了。森神为了cha掉强神于是要加入另外的约束:

进攻方的棋子必须在整个路径的1/4,2/4,3/4处分别吃掉三个防守棋子。(如果路径长度不是4的倍数则下取整)

强神又A了,他觉得这个很无聊,所以现在想让聪明的你计算一共一下有多少种方法来完成对森神的“进攻”。

输入

输入包含若干组测试用例,每组测试用例的第一行包含两个整数n和m(2<=n,m<=8),分别表示棋盘的长与宽,接下来一行有6个整数x1,y1,x2,y2,x3,y3, 0<=xi<n, 0<=yi<m, i=1,2,3代表森神三个棋子摆放的位置(保证(xi,yi)不在起点和终点)

最后一组包含两个0,代表输入结束

输出

输出从(0,0)出发,在整个路径的1/4,2/4,3/4处按输入的顺序吃掉三个棋子最后走到(0,1)的方法数。

样例输入

3 6

2 1 2 4 0 4

4 3

2 0 3 2 0 2

0 0

样例输出

Case 1: 2

Case 2: 0

提示:

对于第一组测试用例,如下图是一种方法,该路径在1/4处(18/4 = 4)吃掉(2,1),在2/4处(18x2/4=9)吃掉(2,4),在3/4处(18x3/4=13)吃掉(0,4)。

3.解题思路:

由于本题的输入数据不大,因此可以使用搜索+剪枝的方法解题。
考虑以下剪枝:
1. 没到1/4,2/4,3/4的步数时,一定不可以出现在防守棋子的格子上
2. 因为格子不能重复走,所以未走过的格子一定不能被分成两部分

3.如果当前步数加上该点到的下一个目标点的曼哈顿距离(最小距离)都大于要求的步数的话,那么就没有必要搜下去了。

4. 5000ms代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define maxn 100100
#define lson root << 1
#define rson root << 1 | 1
#define lent (t[root].r - t[root].l + 1)
#define lenl (t[lson].r - t[lson].l + 1)
#define lenr (t[rson].r - t[rson].l + 1)
#define N 1111
#define eps 1e-6
#define pi acos(-1.0)
#define e exp(1.0)
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
typedef unsigned long long ull;
int mp[9][9];
bool vis[9][9];
int n, m;
int dir[4][2] = { {1, 0}, {0, 1}, {-1, 0}, {0, -1} };
int cpx[5], cpy[5], cps[5];
int cpn[9][9];
int cnt;
void dfs(int sx, int sy, int step, int id)
{
	if (step == cps[id])
	{
		if (cpn[sx][sy] == id) 
			id++;
		else return;
	}
	else if (cpn[sx][sy] > 0) 
		return;
	if (step == cps[4]) 
	{
		cnt++;
		return;
	}
	if (step > cps[id])
		return;
	if (abs(cpx[id] - sx) + abs(cpy[id] - sy) + step > cps[id]) 
		return;
	vis[sx][sy] = 1;
	for (int i = 0; i < 4; i++)
	{
		int dx = sx + dir[i][0];
		int dy = sy + dir[i][1];
		if (dx >= 0 && dx < n && dy >= 0 && dy < m && !vis[dx][dy])
		{
			if ((dx == 0 || vis[dx - 1][dy]) && (dx == n - 1 || vis[dx + 1][dy]) && (dy != 0 && !vis[dx][dy - 1]) && (dy != m - 1 && !vis[dx][dy + 1]))
				continue;
			if ((dx != 0 && !vis[dx - 1][dy]) && (dx != n - 1 && !vis[dx + 1][dy]) && (dy == 0 || vis[dx][dy - 1]) && (dy == m - 1 || vis[dx][dy + 1]))
				continue;
			dfs(dx, dy, step + 1, id);
		}
	}
	vis[sx][sy] = 0;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
	long _begin_time = clock();
#endif
	int kase = 0;
	while (~scanf("%d%d", &n, &m), n + m)
	{
		cnt = 0;
		memset(cpn, 0, sizeof(cpn));
		memset(vis, 0, sizeof(vis));
		for (int i = 1; i <= 3; i++) 
		{
			scanf("%d%d", &cpx[i], &cpy[i]);
			cpn[cpx[i]][cpy[i]] = i;
			cps[i] = n*m*i / 4;
		}
		cpx[4] = 0;
		cpy[4] = 1;
		cpn[0][1] = 4;
		cps[4] = n*m;
		dfs(0, 0, 1, 1);
		printf("Case %d: %d\n", ++kase, cnt);
	}
#ifndef ONLINE_JUDGE
	long _end_time = clock();
	printf("time = %ld ms.", _end_time - _begin_time);
#endif
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值