zoukankan      html  css  js  c++  java
  • SGU 167 I-country

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

    题目

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

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

    问该如何这些选择方格,$1leqslant N,Mleqslant 15, 0leqslant kleqslant 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 imes4)=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
    ", 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
    ", ans);
    	if(ans) print(ai, k, al, ar, adl, adr); //答案为0的时候没有方块
    	return 0;
    
    }
    
  • 相关阅读:
    Eclipse+Java+OpenCV246人脸识别
    Eclipse+Java+OpenCV246环境搭建和代码测试
    Request对象和Response对象详解
    request与response对象详述
    request对象和response对象
    减少HTTP请求之将图片转成二进制并生成Base64编码,可以在网页中通过url查看图片(大型网站优化技术)
    base64码通过http传输 +号变 空格 问题解决
    RSA Android加密的数据服务器上无法解密?
    RSA解密时javax.crypto.BadPaddingException: Data must start with zero
    SRA解密报错:Data must start with zero
  • 原文地址:https://www.cnblogs.com/sahdsg/p/12397412.html
Copyright © 2011-2022 走看看