DTOJ#5128. 棋盘

给定一个 n × n n \times n n×n 的网格,第 i i i 行第 j j j 列记为 ( i , j ) (i, j) (i,j)。每个格子内要么有一枚黑棋,要么有一枚白棋,要么不存在棋子,并且每一枚棋子都有一个权值 w i w_i wi。你现在可以进行两种操作:

  1. 选择两枚异色棋子 x , y x, y x,y,要求它们的左下方均不存在棋子,并将它们移除,代价为 ∣ w x − w y ∣ \lvert w_x − w_y\rvert wxwy。一枚位于 ( a , b ) (a, b) (a,b) 的棋子左下方不存在棋子指不存在其它棋子位于 ( c , d ) (c, d) (c,d) 且满足 a ≤ c , b ≤ d a \leq c, b \leq d ac,bd

  2. 任意选择一枚棋子 x x x,将其移除,代价为 w x w_x wx

求最小需要多少代价,才能将所有棋子移除。

第一行包含一个整数 n n n 表示棋盘大小。

接下来 n n n 行每行一个长度为 n n n 的字符串,第 i i i 行第 j j j 个字符表示网格 ( i , j ) (i, j) (i,j) 的状态(W 表示存在一枚白子,B 表示存在一枚黑子,. 表示不存在棋子)。

接下来 n n n 行每行 n n n 个整数, 第 i i i 行第 j j j 个整数 w i , j w_{i,j} wi,j 表示位于 ( i , j ) (i, j) (i,j) 上的棋子权值。

若改网格内不存在棋子,则 w i , j = 0 w_{i,j} = 0 wi,j=0

输出一行一个整数,表示答案。

样例输入
4
WBB.
BWBW
WBWW
BBBW
1 1 1 0
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
3

对于所有数据,保证, 0 ≤ w i , j ≤ 1 0 6 0 \leq w_{i,j} \leq 10^6 0wi,j106

对于 20 % 20 \% 20% 的数据, n ≤ 4 n \leq 4 n4

对于另外 10 % 10 \% 10% 的数据, 所有存在棋子的网格满足 w i , j = 1 w_{i,j} = 1 wi,j=1

对于 100 % 100 \% 100% 的数据, 1 ≤ n ≤ 12 1 \leq n \leq 12 1n12

可知任意时刻可以匹配的点只有 n n n个,且 2 操作任意时刻操作都行。所以我们只记录 1 操作。
即维护左下到右上的轮廓线,每次枚举两个点转移。
时间复杂度 O ( C 2 n n × n 2 ) O(C_{2n}^{n}\times n^2) O(C2nn×n2)
注:枚举二进制数,1 数量固定可以直接枚举,不必全部枚举。

#include<bits/stdc++.h>
#define N 15
typedef long long ll;

using namespace std;
int inf=1e9;
int h[1<<25];
ll st,to,e[N<<1];
inline ll read(){
	ll x=0;char s=getchar();
	while(s<'0'||s>'9')s=getchar();
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	return x;
}
inline ll nex(ll st){
	ll b=st&(-st),t=st+b,c=t^(t-1),m=(c>>2)/b;
	return t|m;
}
int w[N][N],c[N][N];
ll tmp,ntmp;

int main(){
	e[0]=1;int n=read();
	for(int i=1;i<N<<1;++i)e[i]=e[i-1]<<1;
	for(int i=n;i<2*n;++i)to+=e[i];h[st]=0;
	for(int i=0;i<n;++i)st+=e[i];
	for(int i=1;i<=n;++i){
		char s[N];scanf("%s",1+s);
		for(int j=1;j<=n;++j){
			if(s[j]=='W')c[i][j]=1;
			if(s[j]=='B')c[i][j]=2;
			if(s[j]=='.')c[i][j]=0;
		}
	}
	for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)w[i][j]=read();
    memset(h,0x3f,sizeof(h));
    h[st]=0;
	for(;st!=to;st=nex(st)){
		int i=0,j=0;
		for(int k=2*n-1;k+1;--k){
			if(!((st>>k)&1))++i;
			else{
				++j;
				if(!((st>>(k+1))&1)){
					tmp=(st^(e[k+1]+e[k]));
					h[tmp]=min(h[tmp],h[st]+w[i][j]);
				}
			}
		}
		int li=0,lj=0,ri=0,rj=0;
		for(int lk=2*n-1;lk+1;--lk){
			if(!((st>>lk)&1))++li;
			else{
				++lj;
				if(!c[li][lj])continue;
				if(!((st>>(lk+1))&1)){
					tmp=(st^(e[lk+1]+e[lk]));
					ri=li,rj=lj;
					for(int rk=lk-1;rk+1;--rk){
						if(!((st>>rk)&1))++ri;
						else{
							++rj;
							if((c[li][lj]^c[ri][rj])!=3)continue;
							if(!((st>>(rk+1))&1)){
								ntmp=(tmp^(e[rk+1]+e[rk]));
								h[ntmp]=min(h[ntmp],h[st]+abs(w[ri][rj]-w[li][lj]));
							}
						}
					}
				}
			}
		}
	}
	printf("%d\n",h[to]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值