2011 ACM/ICPC 北京赛区现场赛解题:GemAnd Prince

博客详细介绍了如何解决2011年ACM/ICPC北京赛区竞赛中的一道问题GemAnd Prince。作者使用模拟加搜索的策略,通过广度优先搜索(BFS)穷举每个局面的下一步消除宝石的所有可能性,并在发现有效连通块时采用深度优先搜索(DFS)深入。为了优化搜索效率,采用了剪枝技术,根据已得分和剩余宝石来限制可能的最大得分,从而大幅减少搜索树的规模。尽管这种剪枝方法在大多数情况下能有效减少99.9%的节点,但存在不稳定性,可能无法应对所有数据。提供的代码实现了这一解题策略。
摘要由CSDN通过智能技术生成

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4090

模拟+搜索:对于每一个局面,用bfs穷举出下一步所有可能存在的消除宝石的方法,每发现这样的一个方法就dfs进入下一层。

(即通过floodfill的方法进行bfs,每得到一个大小大于2的连通块的时候就dfs递归深入下去。这样可以让下面的剪枝得到很好的效果,可以将搜索树的size大大减小。)

剪枝:对于每一个局面,记录它通过之前的步骤已经得到的分数,然后维护它还可能得到的最大的分数(比如已经得到了120分,还有5个红色宝石,4个蓝色宝石,2个黑色宝石,那此局面可能得到的分数一定不超过120+5*5+4*4),通过用这个分数与当前已经得到的最大分数进行比较来进行剪枝。对于大多数数据这个剪枝能剪掉99.9%的节点,所以有时可以跑过10*9的数据,但是这个剪枝也是不稳定的,存在不少数据能把它卡掉。不过对于题目的最大上限8*8,这个剪枝已经足够了。

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

int n, m, k;
int mp[10][10];
bool v2[10][10];
int xx[8] = {0, 0, 1, -1, 1, -1, 1, -1};
int yy[8] = {1, -1, 0, 0, 1, -1, -1, 1};
int cnt, ans, sum;

bool in(int x,  int y)
{
	if (x <= n && x >= 1 && y <= m && y >= 1)
	  return true;
	return false;
}

void dfs(int x, int y, int tt[10][10], int p, bool vv[10][10])
{
	vv[x][y] = 1;
	cnt++;
	for (int i = 0; i < 8; ++i)
	{
		if (in(x + xx[i], y + yy[i]) && !vv[x + xx[i]][y + yy[i]] && tt[x + xx[i]][y + yy[i]] == p)
		{
			dfs(x + xx[i], y + yy[i], tt, p, vv);
		}
	}
}

void dfs2(int x, int y, int tt[10][10], int p)
{
	v2[x][y] = 1;
	tt[x][y] = 0;
	for (int i = 0; i < 8; ++i)
	{
		if (in(x + xx[i], y + yy[i]) && !v2[x + xx[i]][y + yy[i]] && tt[x + xx[i]][y + yy[i]] == p)
		{
			dfs2(x + xx[i], y + yy[i], tt, p);
		}
	}
}

void move(int tt[10][10])
{
	int i, j, a[10], cc;
	for (i = 1; i <= 8; ++i)
	  a[i] = 0;
	for (i = 1; i <= m; ++i)
	{
		cc = 0;
		for (j = 1; j <= n; ++j)
		  if (tt[j][i] > 0)
		  {
			  a[++cc] = tt[j][i];
		  }
		for (j = 1; j <= n - cc; ++j)
		  tt[j][i] = 0;
		for (j = 1; j <= cc; ++j)
		{
			tt[j + n - cc][i] = a[j];
		}
	}
	for (j = 1; j <= m; ++j)
	{
		cc = 0;
		for (i = 1; i <= n; ++i)
		  if (tt[i][j] > 0) cc++;
		a[j] = cc;
	}
	int tp[10];
	for (i = 1; i <= n; ++i)
	{
		int tc = 0;
		for (j = 1; j <= m; ++j)
		  if (a[j] == 0)
		  {
			  continue;
		  }
		  else
		  {
			  tp[++tc] = tt[i][j];
		  }
		for (j = 1; j <= tc; ++j)
		  tt[i][j] = tp[j];
		for (j = tc + 1; j <= m; ++j)
		  tt[i][j] = 0;
	}
}

void click(int tt[10][10], int s, int *a, int x, int y)
{
	int i, j;
	int ta[7], tmp[10][10];
	bool vv[10][10];
	int ts = s;
	for (i = 1; i <= k; ++i)
	{
		if (a[i] >= 3)
		  ts += a[i] * a[i];
	}
	if (ts < ans) return;
	for (int ii = 1; ii <= n; ++ii)
	{
		for (int jj = 1; jj <= m; ++jj)
		{
			vv[ii][jj] = 0;
			v2[ii][jj] = 0;
			tmp[ii][jj] = tt[ii][jj];
		}
	}
	if (x != 0 && y != 0)
	  dfs2(x, y, tmp, tmp[x][y]);
	move(tmp);
	for (i = 1; i <= n; ++i)
	{
		for (j = 1; j <= m; ++j)
		{
			if (!vv[i][j] && tmp[i][j] > 0)
			{
				for (int ii = 1; ii <= k; ++ii)
				  ta[ii] = a[ii];
				cnt = 0;
				dfs(i, j, tmp, tmp[i][j], vv);
				if (cnt >= 3)
				{
					ta[tmp[i][j]] -= cnt;
					click(tmp, s + cnt * cnt, ta, i, j);
				}
			}
		}
	}
	ans = max(ans, s);
}

int main()
{
	int i, j, a[7];
	while (scanf("%d%d%d", &n, &m, &k) != EOF)
	{
		memset(a, 0, sizeof(a));
		memset(v2, 0, sizeof(v2));
		for (i = 1; i <= n; ++i)
		{
			for (j = 1; j <= m; ++j)
			{
				scanf("%d", &mp[i][j]);
				if (mp[i][j] > 0)
				  a[mp[i][j]]++;
			}
		}
		ans = 0;
		click(mp, 0, a, 0, 0);
		printf("%d\n", ans);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值