51nod 1487 占领资源(RMQ+暴力)

题目来源:  TopCoder
基准时间限制:1 秒 空间限制:131072 KB 分值: 40  难度:4级算法题
 收藏
 关注
有一个矩形区域被划分为N行M列的网格,每个格子里有一定数量的资源并记录在矩阵val中,坐标(x,y)位置上资源量为val[x][y],其val中每个元素的值为0~9的整数。如果你在某个网格(a,b)上造一座保护塔,那么你可以占领K个网格中的资源,这K个格子分别是(a+dx[1],b+dy[1]),(a+dx[2],b+dy[2]),...,(a+dx[K],b+dy[K]),注意(a,b)这格本身可能未必会被占领。现在你能建造不超过2个塔,问最多能占领多少资源?一个网格被多个塔占领时其资源只计算一次。另外如果计算的位置(a+dx[i],b+dy[i])在网格外,则不贡献任何资源。
Input
多组测试数据,第一行一个整数T,表示测试数据数量,1<=T<=5
每组测试数据有相同的结构构成:
每组数据第一行三个整数N,M,K,其中2<=N,M<=100,1<=K<=10。
之后会有N行,每行M个元素,表示val矩阵。每个元素为0~9,占一个字符,元素间没空格。
再接下来有K行,每行两个整数dx[i]与dy[i],其中-(N-1)<=dx[i]<=N-1,-(M-1)<=dy[i]<=(M-1).
Output
每组数据一行输出,即可占领的最大资源总量。
Input示例
3
2 2 2
11
11
0 0
0 1
2 2 2
11
11
0 0
1 1
2 2 1
15
61
0 0
Output示例
4
3
11
孔炤  (题目提供者)
C++的运行时限为:1000 ms ,空间限制为:131072 KB  示例及语言说明请按这里

 允许其他 AC 的用户查看此代码,分享代码才能查看别人的代码并有机会获得勋章


题解:最浅显的做法是枚举两个保护塔,然后求解,复杂度是O(n^2*m^2*k^2)。。。。算了吧

呢还能怎么搞哇。真的是难倒我了,想了半天也没有合适的姿势,只好看了发题解。

然后看了:http://blog.csdn.net/qwb492859377/article/details/51088928大牛的博客,才有了思路。

具体就是枚举一个保护塔的位置,然后处理出来所有和这个保护塔有所冲突的位置以及不冲突的位置。

有所冲突的位置不会超过k^2个,所以暴力就行,不冲突的位置我们可以考虑用RMQ。。。

总复杂度为O(n*m*k^3)....可以过。。。

#include<set>  
#include<map>         
#include<stack>                
#include<queue>  
#include<vector>        
#include<string>      
#include<math.h> 
#include<time.h>  
#include<stdio.h>                
#include<iostream>                
#include<string.h>                
#include<stdlib.h>        
#include<algorithm>       
#include<functional>        
using namespace std;
typedef long long ll;
#define inf 1000000000           
#define mod 1000000007                 
#define maxn  20005    
#define PI 3.1415926  
#define lowbit(x) (x&-x)     
#define eps 1e-9
char s[105][105];
int n, m, k, a[maxn][15], val[maxn], q[maxn];
int w[maxn], vis[105][105], b[maxn / 100], c[maxn / 100];
int f(int x, int y)
{
	return (x - 1)*m + y;
}
void RMQ(int len)
{
	int i, j;
	for (i = 1;i <= len;i++)
		a[i][0] = w[i];
	for (j = 1;(1 << j) <= len;j++)
		for (i = 1;i + (1 << j) - 1 <= len;i++)
			a[i][j] = max(a[i][j - 1], a[i + (1 << (j - 1))][j - 1]);
}
int findmax(int l, int r)
{
	int len = 0;
	while ((1 << (len+1)) <= r - l + 1)
		len++;
	return max(a[l][len], a[r - (1 << len) + 1][len]);
}
int solve()
{
	int ans = 0, i, j, h, t;
	for(i=1;i<=n;i++)
		for (j = 1;j <= m;j++)
		{
			int mx = 0, cnt = 0, id = f(i, j);
			for (h = 1;h <= k;h++)
			{
				int dx = i + b[h];
				int dy = j + c[h];
				if (dx<1 || dx>n || dy<1 || dy>m)
					continue;
				vis[dx][dy] = id;
				for (t = 1;t <= k;t++)
				{
					int x = dx - b[t];
					int y = dy - c[t];
					if (x<1 || x>n || y<1 || y>m)
						continue;
					q[++cnt] = f(x, y);
				}
			}
			q[++cnt] = 0;
			q[++cnt] = n*m + 1;
			sort(q + 1, q + cnt + 1);
			cnt = unique(q + 1, q + cnt + 1) - q - 1;
			for (h = 1;h < cnt;h++)
				if (q[h] + 1 <= q[h + 1] - 1)
					mx = max(mx, findmax(q[h] + 1, q[h + 1] - 1));
			for (h = 2;h < cnt;h++)
			{
				int x = (q[h] - 1) / m + 1;
				int y = (q[h] - 1) % m + 1;
				int sum = w[f(x, y)];
				for (t = 1;t <= k;t++)
				{
					int dx = x + b[t];
					int dy = y + c[t];
					if (dx<1 || dx>n || dy<1 || dy>m)
						continue;
					if (vis[dx][dy] == id)
						sum -= s[dx][dy]-'0';
				}
				mx = max(mx, sum);
			}
			ans = max(ans, mx + w[id]);
		}
	return ans;
}
int main(void)
{
	int T, i, j, h;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d%d", &n, &m, &k);
		for (i = 1;i <= n;i++)
			for (j = 1;j <= m;j++)
				vis[i][j] = w[f(i, j)] = 0;
		for (i = 1;i <= n;i++)
			scanf("%s", s[i] + 1);
		for (i = 1;i <= k;i++)
			scanf("%d%d", &b[i], &c[i]);
		for(i=1;i<=n;i++)
			for (j = 1;j <= m;j++)
			{
				int id = f(i, j);
				for (h = 1;h <= k;h++)
				{
					int dx = i + b[h];
					int dy = j + c[h];
					if (dx<1 || dx>n || dy<1 || dy>m)
						continue;
					w[id] += s[dx][dy] - '0';
				}
			}
		RMQ(n*m);
		printf("%d\n", solve());
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值