A Rectangular Barn

题意:摘自NOCOW翻译(http://www.nocow.cn/index.php/Translate:USACO/rectbarn

描述

到底是个资本家,Farmer John想通过买更多的奶牛来扩大它的生意。它需要给奶牛建造一个新的牛棚。 FJ买了一个矩形的R(1 <= R <= 3000)行C(1 <= C <= 3000)列的牧场。不幸的是,他发现某些1 x 1的区域被损坏了,所以它不可能在把整个牧场建造成牛棚了。 FJ数了一下,发现有P(1 <= p <= 30000)个1 x 1的损坏区域并且请你帮助他找到不包含损坏区域的面积最大的矩形的牛棚。

  • 对同一个损坏区域可能有多次描述

题目名称:rectbarn

[编辑]输入格式(file rectbarn.in)

第1行: 三个空格隔开的整数 R, C, and P.

第2..P+1行: 每行包含两个空格隔开的整数, r和c, 给出一个损坏区域的行号和列号.

[编辑]输出格式(file rectbarn.out)

第1行: 牛棚的最大可能面积

[编辑]样例输入

3 4 2
1 3
2 1

[编辑]样例输出

6

[编辑]输出解释

  1 2 3 4
.+-+-+-+-+
1| | |X| |
.+-+-+-+-+
2|X|#|#|#|
.+-+-+-+-+
3| |#|#|#|
.+-+-+-+-+

标'X'的区域是损坏的, 标 '#'的区域是牛棚.

解题思路:

  1. DP过程参考NOCOW中的解题思路“DP”,转载一下
  2. DP

    设h[i,j]为点(i,j)向上方扩展的最大高度,l[i,j]为(i,h[i,j])这条线段向左边扩展的最长距离,r[i,j]为(i,h[i,j])向右边扩展的最长距离。 转移方程: Usaco rectbarn.JPG

    其中,tl表示点(i,j)向左扩展的最大距离,tr表示点(i,j)向右扩展的最大距离。


  3. 这题内存要求比较高,所以用滚动数组
  4. 用obstacle[1...P][2]存储损坏区域的坐标,然后按照坐标(i, j)递增(i优先级大于j)排序
  5. 按照行从1到R进行DP。处理每一行时用链表(因为obstacle已经排序过了,所以可以用数组很方便的实现)表示该行的损坏点,只用记录列序号。为了避免对边界的判定,所以在每一行加入0和C+1这两个损坏点。
  6. 在每一行中依次处理j = [1, C],因为有了链表形式的损坏点序列,所以找到第一个序号不小于j的损坏点(设序号为index)。如果index == j,则意味着j是损坏点。如果index > j,那么序号为index - 1和index的点就将j夹在中间,可以很方便地求出DP中所需的 tl和tr
  7. 在DP的过程中保存面积的最大值即可,面积表示为h[i, j] * (l[i, j] + r[i, j] - 1)

代码

/*
ID: zc.rene1
LANG: C
PROG: rectbarn
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX 3000
#define MAX_P 30000
#define INFINITY 10000

int R, C, P;
int obstacle[MAX_P + 1][2];
int ob_index;
int h[MAX + 1];
int l[MAX + 1];
int r[MAX + 1];
int global_max = 0;

int min(int a, int b)
{
    return a < b ? a : b;
}

int cmp(const void *a, const void *b)
{
    return (((int *)a)[0] * C + ((int *)a)[1]) - (((int *)b)[0] * C + ((int *)b)[1]);
}

void Initial(FILE *fin)
{
    int i, j, k;

    fscanf(fin, "%d %d %d", &R, &C, &P);

    for (k=1; k<=P; k++)
    {
	fscanf(fin, "%d %d", &i, &j);
	obstacle[k][0] = i;
	obstacle[k][1] = j;
    }

    qsort(obstacle + 1, P, 2 * sizeof(int), cmp);
    ob_index = 1;

    memset(h, 0, sizeof(h));
    for (k=1; k<=C; k++)
    {
	l[k] = INFINITY;
	r[k] = INFINITY;
    }
}

void GetChain(int i, int *chain)
{
    memset(chain, 0, (MAX + 2) * sizeof(int));

    chain[++chain[0]] = 0;

    while (obstacle[ob_index][0] == i && ob_index <= P)
    {
	chain[++chain[0]] = obstacle[ob_index++][1];
    }

    chain[++chain[0]] = C + 1;
}

void DP(void)
{
    int i, j, index, temp;
    int chain[MAX + 2];

    for (i=1; i<=R; i++)
    {
	GetChain(i, chain);
	index = 1;
	for (j=1; j<=C; j++)
	{
	    while (chain[index] < j && index <= chain[0])
	    {
		index++;
	    }

	    if (chain[index] == j)
	    {
		h[j] = 0;
		l[j] = INFINITY;
		r[j] = INFINITY;
	    }
	    else
	    {
		h[j]++;
		l[j] = min(l[j], j - chain[index - 1]);
		r[j] = min(r[j], chain[index] - j);

		temp = h[j] * (l[j] + r[j] - 1);

		if (temp > global_max)
		{
		    global_max = temp;
		}
	    }
	}
    }
}

int main(void)
{
    FILE *fin, *fout;

    fin = fopen("rectbarn.in", "r");
    fout = fopen("rectbarn.out", "w");

    Initial(fin);
    DP();
    fprintf(fout, "%d\n", global_max);

    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值