[BZOJ 3753]Wall

一类维护连通性的DP

要将图进行转化。


3.1 题目描述
众所周知,GFW 的出现促进了社会的和谐。Wayne 于是想研究一下GFW。
Wayne 喜欢网格,所以他把一些网站排成了N M 的网格,每个格子代表一个网站。每个网
站有一个评分,当然评分可正可负。现在Wayne 想用一堵“墙”围住一些网站,使得评分和最大。
所谓“墙”,就是一个由网格的边组成的简单多边形,不能自交,也不能有空洞。
过了一会儿Wayne 发现这个模型太一般了。出于一些原因,有些网站必须在“墙”外,我们称之
为“坏网站”;而有些网站必须在“墙”内,我们称之为“好网站”。当然了,“坏网站”的评分不一定
低,“好网站”的评分不一定高。Wayne 又想知道,这种情况下,能够得到的最大评分和。注意,并
不总是存在合法的方案,所以当无法实现时,输出“Can not establish GFW.”。
3.2 输入格式
第一行两个正整数N 和M。
接下来N 行,每行M 个用空格隔开的整数,表示网站的权值。
最后N 行,每行M 个用空格隔开的整数,1 表示坏网站,2 表示好网站,0 表示其他网站。
3.3 输出格式
输出两行,第一行一个整数表示一堵“墙”能围住的最大评分和,第二行表示有限制时的答案。
3.4 样例输入
3 3
2 -1 2
-3 100 -3
-3 20 -3
2 0 2
0 1 0
0 0 0
3.5 样例输出
123
17
3.6 数据规模
对于20% 的数据,N = 1 或M = 1。
对于另外30% 的数据,N <= 3,M <= 3。
对于100% 的数据,N <= 10,M <= 10,权值的绝对值不超过106,且至少一个权值非负,至少一
个好网站。



#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define maxn 20
using namespace std;

const int inf = 0x7fffffff;

int ans;

typedef long long ll;

int n, m;

int a[maxn][maxn], need[maxn][maxn];

int QAQ, qaq;

struct HashMap{
	#define N 2000010
	#define mod 997
	int f[N], h[mod + 10], nxt[N], size;
	ll st[N];

	void init(){
		memset(h, 0, sizeof h);
		size = 0;
	}

	void push(ll val, int ans){
		int tmp = val % mod;
		for(int i = h[tmp]; i; i = nxt[i]){
			if(st[i] == val){
				f[i] = max(f[i], ans);
				return;
			}
		}
		++ size;
		f[size] = ans;
		st[size] = val;
		nxt[size] = h[tmp];
		h[tmp] = size;
	}
}dp[2];

int code[maxn], ch[maxn];

void Decode(ll st){
	for(int i = m; i >= 0; i --){
		code[i] = st & 7;
		st >>= 3;
	}
}

ll Encode(){
	ll st = 0;
	int cnt = 0;
	memset(ch, -1, sizeof ch);
	ch[0] = 0;
	for(int i = 0; i <= m; i ++){
		if(ch[code[i]] == -1)
		    ch[code[i]] = ++ cnt;
		code[i] = ch[code[i]];
		st <<= 3;
		st |= code[i];
	}
	return st;
}

void Shift(){
	for(int i = m; i >= 1; i --)
	    code[i] = code[i - 1];
	code[0] = 0;
}

bool Judge(int cnt, int i, int j){
	if(cnt && need[i][j] == 1)return false;
	if(!cnt && need[i][j] == 2)return false;
	return true;
}

void DP(int cur, int i, int j){
	dp[cur ^ 1].init();
	for(int k = 1; k <= dp[cur].size; k ++){
		Decode(dp[cur].st[k]);
		if(j == 1){
			if(code[m])continue;
			Shift();
		}
		int left = code[j - 1], up = code[j];
		bool cnt = 0;
		for(int p = 0; p < j - 1; p ++)
		    cnt ^= code[p] != 0;
		if(left && up){
			if(left == up){
				code[j] = code[j - 1] = 0;
				ll tmp = Encode();
				if(tmp == 0 && QAQ == qaq)ans = max(ans, dp[cur].f[k]);
			}
			else{
				if(Judge(cnt, i, j)){
					code[j] = code[j - 1] = 0;
					for(int p = 0; p <= m; p ++)
					    if(code[p] == up)
					        code[p] = left;
					dp[cur ^ 1].push(Encode(), dp[cur].f[k] + cnt * a[i][j]);
				}
			}
		}
		else if(left || up){
			int tmp = max(left, up);
			if(Judge(cnt, i, j)){
				code[j - 1] = 0;
				code[j] = tmp;
				dp[cur ^ 1].push(Encode(), dp[cur].f[k] + cnt * a[i][j]);
			}
			cnt ^= 1;
			if(Judge(cnt, i, j)){
				code[j - 1] = tmp;
				code[j] = 0;
				dp[cur ^ 1].push(Encode(), dp[cur].f[k] + cnt * a[i][j]);
			}
		}
		else{
			if(Judge(cnt, i, j)){
				code[j - 1] = code[j] = 0;
				dp[cur ^ 1].push(Encode(), dp[cur].f[k] + cnt * a[i][j]);
			}
			cnt ^= 1;
			if(Judge(cnt, i, j)){
				code[j - 1] = code[j] = 15;
				dp[cur ^ 1].push(Encode(), dp[cur].f[k] + cnt * a[i][j]);
			}
		}
	}
}

void Solve(){
	int cur = 0;
	dp[cur].init();
	dp[cur].push(0, 0);
	ans = -inf;
	for(int i = 1; i <= n; i ++){
		for(int j = 1; j <= m; j ++){
			DP(cur, i, j);
			cur ^= 1;
			if(need[i][j] == 2)
			    qaq ++;
		}
	}
	if(ans == -inf)
		printf("Can not establish GFW.\n");
    else printf("%d\n",ans);
}

int main(){
	scanf("%d%d", &n, &m);
	n ++, m ++;
	for(int i = 1; i < n; i ++)
		for(int j = 1; j < m; j ++)
			scanf("%d", &a[i][j]);

	Solve();

	for(int i = 1; i < n; i ++)
		for(int j = 1; j < m; j ++){
            scanf("%d", &need[i][j]);
            if(need[i][j] == 2)
                QAQ ++;
		}

	Solve();
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值