一类维护连通性的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;
}