【题解】CH5104 I-country 线性DP

在N*M的矩阵中,寻找一个包含K个凸连通块以最大化权值和,输出最大权值和及占有的格子坐标。采用线性DP解决,状态转移方程包括四种情况,时间复杂度为O(N^2M^5)。
摘要由CSDN通过智能技术生成

题目链接

描述

在 N*M 的矩阵中,每个格子有一个权值,要求寻找一个包含 K 个格子的凸连通块(连通块中间没有空缺,并且轮廓是凸的,如书中图片所示),使这个连通块中的格子的权值和最大。求出这个最大的权值和,并给出连通块的具体方案。本题有SPJ,输出任意一种方案即可。N,M≤15,K≤225。

According to top-secret A-country plans, I-country is divided into N*M equal squares, each square contains some oil resources. They want to occupy all the territory of I-country, but the UN (United Nations) will allow them to occupy only K squares. Of course, A-country want to get control over as many oil as possible, but, they will have to guard all their territory. So, they need their territory to be easy-controlled, i.e. from any square to any it must be possible to get moving only along two directions (selected from the next list: left, right, up, down; for different squares pairs of directions may differ).
You are to write a program, which determinies, what squares will be occupyed by A-country. If there are several solutions, you may output any.

输入格式

On the first line of input there are 3 integer numbers N,M,K (1<=N,M<=15, 0<=K<=N*M). Next N lines contains M integers each, which are the number of oil resource on that square. Each of this numbers lies in range of 0 to 1000.

输出格式

On the first line of output, write string “Oil : X”, where integer number X — the maximal number of oil which can be controlled by A-country. Next you should output K pairs of numbers — coordinates of the squares which will be occupied by A-country. The first coordinate is number of row (top to bottom, starting from 1), second is number of column (left to right, starting from 1).

样例输入

2 3 4
10 20 30
40 2 3

样例输出

Oil : 100
1 1
1 2
1 3
2 1

本题校验器(SPJ)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std; 
//一些定义
const int ACCEPT = 0;
const int WRONG_ANSWER = 1;
//fstd 标准输出 fout 选手输出 fin 标准输入
FILE *fstd,*fout,*fin;
int n, m, k, a[20][20], v[20][20], ans, val;

//执行比较操作。
bool DoCompare(){
    fscanf(fin, "%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; i++)
    	for (int j = 1; j <= m; j++) fscanf(fin, "%d", &a[i][j]);
    fscanf(fstd, "%*s%*s%d", &ans);
    fscanf(fout, "%*s%*s%d", &val);
    // 答案不对 
    if (val != ans) return false;
    for (int i = 1; i <= k; i++) {
    	int x, y; fscanf(fout, "%d%d", &x, &y);
    	// 格子越界或者有重复 
    	if (x < 1 || y < 1 || x > n || y > m || v[x][y]) return false;
    	v[x][y] = 1;
    	val -= a[x][y];
    }
    // 输出的格子加起来不等于答案 
    if (val) return false;
    // 检查凸性 
    for (int i = 1; i <= n; i++) {
    	int cnt = 0, l = m, r = 1;
    	for (int j = 1; j <= m; j++)
    		if (v[i][j]) cnt++, l = min(l, j), r = max(r, j);
    	if (cnt == 0) continue;
    	// 输出的格子在一行里不连续 
    	if (r - l + 1 != cnt) return false;
    }
    for (int j = 1; j <= m; j++) {
    	int cnt = 0, l = n, r = 1;
    	for (int i = 1; i <= n; i++)
    		if (v[i][j]) cnt++, l = min(l, i), r = max(r, i);
    	if (cnt == 0) continue;
    	// 输出的格子在一列里不连续 
    	if (r - l + 1 != cnt) return false;
	}
	return true;
}

int main(int argc, char* argv[])
{
    if(argc!=4){
        printf("参数不足 %d",argc);
        return -1;
    }

    //打开文件
    if(NULL==(fstd=fopen(argv[1],"r"))){
        return -1;
    }
    if(NULL==(fout=fopen(argv[2],"r"))){
        return -1;
    }
    if(NULL==(fin=fopen(argv[3],"r"))){
        return -1;
    }

    if(DoCompare()){
        return ACCEPT;
    }else{
        return WRONG_ANSWER;
    }
}

F [ i , j , l , r , x , y ] F[i,j,l,r,x,y] F[i,j,l,r,x,y] 表示前 i i i 行选择了 j j j 个格子,其中第 i i i 行选择了第 l l l r r r 个格子,左边界状态为 x x x,右边界状态为 y y y x = 0 x=0 x=0 为收缩, x = 1 x=1 x=1 为扩张, y y y同理),能构成的凸连通块的最大权值和。
状态转移方程:
1.左边界扩张,右边界扩张:
F [ i , j , l , r , 1 , 1 ] = ∑ p = l r A [ i , p ] + { F [ i − 1 , 0 , m , 0 , 1 , 1 ] , j = r − l + 1 &gt; 0 max ⁡

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值