USACO 3.3 Camelot

题意:

在R行C列的棋盘上(R<=30,C<=26),有互相不重叠的若干个骑士和王,骑士可以按照马的方式走,王可以走周围的八个位置。

骑士和王到达一个点的时候骑士可以接王。

问:最少经过多少步能够让所有的棋子到达同一格。

思路:

首先棋盘的每两点之间的骑士最短距离可以通过R*C次BFS得到。

考虑暴力枚举:

for 集合点(R*C)

     for 接王的骑士(R*C)

          for 接王的点(R*C)

               算出距离

这样,复杂度达到了O((R*C)^3),必然超时,于是想方设法进行剪枝。

考虑以下几种剪枝方式:(无证明。。。来自From北极星天南)

1)由于王总是比骑士走得慢,所以接王的点一定靠近王(在王的某个范围内)。

2)在枚举集合点的时候,如果所有的骑士到达该点的距离和已经大于当前答案,那么不许进行后面的枚举

3)当骑士较多时,集合点在骑士们的中心附近(骑士均值坐标的某个范围内)。

注意:

交了很多次才过。

1)读入数据的时候,纵坐标可能是两位的数字,不是单个字符,但是横坐标就是单个字符

2)x对应的是column,y对应的是row

3)可能存在不能到达的两个点,所以所有的dist[][][][]要初始化为INF而不是0

4)王一步可以到达周围八个点,所以当王从a,b 到c,d 的最小步数是max(abs(a,b),abs(c-d))

5)只有一个王单独处理,答案是0

Executing...
   Test 1: TEST OK [0.000 secs, 7476 KB]
   Test 2: TEST OK [0.000 secs, 7476 KB]
   Test 3: TEST OK [0.000 secs, 7476 KB]
   Test 4: TEST OK [0.011 secs, 7476 KB]
   Test 5: TEST OK [0.065 secs, 7476 KB]
   Test 6: TEST OK [0.065 secs, 7476 KB]
   Test 7: TEST OK [0.000 secs, 7476 KB]
   Test 8: TEST OK [0.011 secs, 7476 KB]
   Test 9: TEST OK [0.032 secs, 7476 KB]
   Test 10: TEST OK [0.065 secs, 7476 KB]
   Test 11: TEST OK [0.000 secs, 7476 KB]
   Test 12: TEST OK [0.000 secs, 7476 KB]
   Test 13: TEST OK [0.000 secs, 7476 KB]
   Test 14: TEST OK [0.000 secs, 7476 KB]
   Test 15: TEST OK [0.000 secs, 7476 KB]
   Test 16: TEST OK [0.000 secs, 7476 KB]
   Test 17: TEST OK [0.000 secs, 7476 KB]
   Test 18: TEST OK [0.000 secs, 7476 KB]
   Test 19: TEST OK [0.000 secs, 7476 KB]
   Test 20: TEST OK [0.000 secs, 7476 KB]
代码:

/*
ID: jasison2
LANG: C++
TASK: camelot
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define N 32
#define INF 0x7fffffff
#define MAX(a,b) (a)<(b)?(b):(a)
#define MIN(a,b) (a)<(b)?(a):(b)
inline int ABS(int x) { return x<0 ? -x : x; }

int r, c, n;
int dist[N][N][N][N]; // distance with knight
bool vis[N][N];
int p[N*N][2]; // king and knight position
int dx, dy; // gather position
int dir[8][2] = {{-2,-1},{-2,1},{-1,-2},{-1,2},{1,2},{1,-2},{2,-1},{2,1}};
struct Point
{
	int x, y;
	Point() {}
	Point(int _x, int _y): x(_x), y(_y){}
	bool legal() { return x>0 && x<=c && y>0 && y<=r && !vis[x][y]; }
	void visit() { vis[x][y] = true; }
};
inline void input()
{
	cin >> r >> c;
	n = 0;
	char x;
	dx = dy = 0;
	while (cin >> x >> p[n][1])
	{
		p[n][0] = x - 'A' + 1;
		if (n)
		{
			dx += p[n][0];
			dy += p[n][1];
		}
		n ++;
	}
	if (n > 1)
	{
		dx /= (n-1);
		dy /= (n-1);
	}
}
void bfs()
{
	Point cur, nxt;
	for (int i = 1; i <= c; ++ i)
	{
		for (int j = 1; j <= r; ++ j)
		{
			for (int ix = 1; ix <= c; ++ ix)
				for (int iy = 1; iy <= r; ++ iy)
					dist[i][j][ix][iy] = 100;
			memset(vis, false, sizeof(vis));
			queue <Point> Q;
			Q.push(Point(i,j));
			vis[i][j] = true;
			dist[i][j][i][j] = 0;
			while (!Q.empty())
			{
				cur = Q.front();
				Q.pop();
				for (int k = 0; k < 8; ++ k)
				{
					nxt = Point(cur.x+dir[k][0], cur.y+dir[k][1]);
					if (nxt.legal())
					{
						dist[i][j][nxt.x][nxt.y] = dist[i][j][cur.x][cur.y] + 1;
						nxt.visit();
						Q.push(nxt);
					}
				}
			}
		}
	}
}
int calc()
{
	if (n == 1) return 0;
	int ans = INF;
	int sdx, sdy, edx, edy, sx, sy, ex, ey;
	if (n < 3)
	{
		sdx = sdy = 1;
		edx = c; edy = r;
	}
	else
	{
		sdx = MAX(1,dx-2); sdy = MAX(1,dy-2);
		edx = MIN(c,dx+2); edy = MIN(r,dy+2);
	}
	sx = MAX(1,p[0][0]-2); sy = MAX(1,p[0][1]-2);
	ex = MIN(c,p[0][0]+2); ey = MIN(r,p[0][1]+2);
	for (dx = sdx; dx <= edx; ++ dx)
	{
		for (dy = sdy; dy <= edy; ++ dy)
		{
			int tans = 0;
			for (int k = 1; k < n; ++ k)
				tans += dist[p[k][0]][p[k][1]][dx][dy];
			if (tans > ans) continue;
			for (int k = 1; k < n; ++ k)
			{
				tans -= dist[p[k][0]][p[k][1]][dx][dy];
				for (int x = sx; x <= ex; ++ x)
				{
					for (int y = sy; y <= ey; ++ y)
					{
						tans += dist[p[k][0]][p[k][1]][x][y] + dist[x][y][dx][dy];
						tans += MAX(ABS(x-p[0][0]), ABS(y-p[0][1]));
						if (tans < ans) ans = tans;
						tans -= dist[p[k][0]][p[k][1]][x][y] + dist[x][y][dx][dy];
						tans -= MAX(ABS(x-p[0][0]), ABS(y-p[0][1]));
					}
				}
				tans += dist[p[k][0]][p[k][1]][dx][dy];
			}
		}
	}
	return ans;
}
void output()
{
	printf("%d\n", calc());
}
int main()
{
	freopen("camelot.in","r",stdin);
	freopen("camelot.out","w",stdout);
	input();
	bfs();
	output();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值