SGU 167 I-country

https://codeforces.com/problemsets/acmsguru/problem/99999/167

题目

A国将I国分成了N*M的方格,联合国只允许A国占领k个方格(鼓励占别人的领土……),每块方格下面有一定的石油,A国想选择k个方格,满足

  • 这k个方格中任意两个方格可以只通过向两种方向(上下左右选择两种,可以交替移动)移动到达,不能经过没有选的方格
  • 这k个方格包含的石油数量最大

问该如何这些选择方格,$1\leqslant N,M\leqslant 15, 0\leqslant k\leqslant NM$

时间 750ms,内存65536kb

题解

方格肯定是连通的,然后还有只能向两种方向移动到达,说明这个连通块不能出现凹进去的情况,也就是必须是凸的

那么,把连通块分成几行,从上往下每一行的左边界先递减,后递增,从上往下每一行的右边界先递增,后递减

令$dp[i][cnt][l][r][dl][dr]$为前i行选择了cnt个方块,第i行选择了l~r的方块,左边界变化的情况是dl,右边界变化的情况是dr。

那么,就有很多转移

  1. 开始
  2. 变化情况不变
  3. 左边界从递减变成递增
  4. 右边界从递增变成递减
  5. 左右边界一起变成><

注意转移的时候依然要保证凸连通性

写了一半发现不能中途结束……于是在转移的时候就更新答案了……但是又忘了开始和k=0的情况

转移需要枚举原来的l和原来的r,同时还需要枚举dl和dr,时间复杂度$\mathcal{O}(NKMM\times4)=\mathcal{O}(4N^5)$,有点悬,但是没有优化常数就过了

由于内存限制得很紧,保存转移的数据都需要改用unsigned char来存储,顺便把dp的第一维减小到2

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
#define MAXN 17
typedef unsigned char uchar;
int dp[2][MAXN*MAXN][MAXN][MAXN][2][2];
uchar prel[MAXN][MAXN*MAXN][MAXN][MAXN][2][2];
uchar prer[MAXN][MAXN*MAXN][MAXN][MAXN][2][2];
uchar predl[MAXN][MAXN*MAXN][MAXN][MAXN][2][2];
uchar predr[MAXN][MAXN*MAXN][MAXN][MAXN][2][2];
int a[MAXN][MAXN], b[MAXN][MAXN];
int n,m,k;
int ans;
uchar ai, al, ar, adl, adr;
inline void update(uchar i, uchar pl, uchar pr, uchar pdl, uchar pdr, 
		uchar nnum, uchar nl, uchar nr, uchar ndl, uchar ndr) {
	if(pl>pr) return;
	if(pl<0 || pr>=m) return;
	int pnum = nnum-(nr-nl+1); if(pnum<pr-pl+1) return;
	int val = dp[(i&1)^1][pnum][pl][pr][pdl][pdr];
	val += b[i][nr]-b[i][nl]+a[i][nl];
	if(val>dp[i&1][nnum][nl][nr][ndl][ndr]) { 
		dp[i&1][nnum][nl][nr][ndl][ndr]=val;
		prel[i][nnum][nl][nr][ndl][ndr]=pl;
		prer[i][nnum][nl][nr][ndl][ndr]=pr;
		predl[i][nnum][nl][nr][ndl][ndr]=pdl;
		predr[i][nnum][nl][nr][ndl][ndr]=pdr;
		if(val>ans) { //更新答案
			ans=val, ai=i, al=nl, ar=nr, adl=ndl, adr=ndr;
		}
	}
}
void print(int i, int num, int l, int r, int dl, int dr) {
	if(l==255) return; //开始
	if(i) print(i-1, num-(r-l+1),
			prel[i][num][l][r][dl][dr],
			prer[i][num][l][r][dl][dr],
			predl[i][num][l][r][dl][dr],
			predr[i][num][l][r][dl][dr]);
	REPE(z,l,r) {
		printf("%d %d\n", i+1, z+1);
	}
}
const int dx[]={-1,1};
int main() {
	scanf("%d%d%d", &n, &m, &k);
	REP(i,0,n) REP(j,0,m) scanf("%d", &a[i][j]);
	REP(i,0,n) b[i][0]=a[i][0];
	REP(i,0,n) REP(j,1,m) b[i][j]=a[i][j]+b[i][j-1];
	memset(dp[1],0,sizeof(dp[1]));
	ans=0;
	REP(i,0,n) {
		memset(dp[i&1],0,sizeof(dp[i&1]));
		
		REP(nl,0,m) for(int nr=nl; nr<m && nr-nl+1<=k; nr++) {
			int val=dp[i&1][nr-nl+1][nl][nr][0][1]=b[i][nr]-b[i][nl]+a[i][nl]; //开始
			prel[i][nr-nl+1][nl][nr][0][1]=255;
			if(val>ans) { //更新答案
				ans=val, ai=i, al=nl, ar=nr, adl=0, adr=1;
			}
			REPE(num,nr-nl+1,k) REP(dl,0,2) REP(dr,0,2) { //保持状态不变
				for(int pl=nl; pl>=0 && pl<m && pl<=nr; pl-=dx[dl]) 
					for(int pr=nr; pr>=0 && pr<m && pr>=pl && pr>=nl; pr-=dx[dr]) {
						update(i, pl, pr, dl, dr, num, nl, nr, dl, dr);
					}
			}
			REPE(num,nr-nl+1,k) {
				PERE(pl,nl,0) PERE(pr,nr,nl) //到 >>
					update(i, pl, pr, 0, 1, num, nl, nr, 1, 1);
				REP(pr,nr,m) REPE(pl,nl,nr) //到 <<
					update(i, pl, pr, 0, 1, num, nl, nr, 0, 0);
				PERE(pl,nl,0) REP(pr,nr,m) { //到 ><
					update(i, pl, pr, 0, 1, num, nl, nr, 1, 0);
					update(i, pl, pr, 0, 0, num, nl, nr, 1, 0);
					update(i, pl, pr, 1, 1, num, nl, nr, 1, 0);
				}
			}
		}

	}
//	int K=(n-1)&1;
	printf("Oil : %d\n", ans);
	if(ans) print(ai, k, al, ar, adl, adr); //答案为0的时候没有方块
	return 0;

}

猜你喜欢

转载自www.cnblogs.com/sahdsg/p/12397412.html
sgu
167