题意:
解法:
把取走棋子的状态压缩为一条轮廓线,可以用长度为2n的二进制数表示每种轮廓线,
因为题目要求删除的棋子左下不能有其他棋子,
那么选择轮廓线从左上到右下,这样的话遇到10拐点(棋子)的时候,左下是没有棋子的.
合法的轮廓线只有C(2n,n)种,
10拐点表示当前位置上有棋子,单独删除进行状态转移,或者枚举另一个10拐点一起删除进行转移,
转移之后10拐点就需要变成01.
下面的是瞎写的:
3x3的网格dp过程中实际上看作4x4的(有虚行和虚列):
目标状态是将10全部消掉,轮廓线变为:
code:
#include <bits/stdc++.h>
using namespace std;
char ch[12][12];
int a[12][12];
int d[1<<24];
int n;
signed main(){
while(scanf("%d",&n)!=EOF){
for(int i=0;i<n;i++){
scanf("%s",ch[i]);
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%d",&a[i][j]);
}
}
//0向右,1向下
int e=(1<<n)-1;//终止状态
int s=(e<<n);//起始状态
//初始化
for(int i=e;i<=s;i++)d[i]=1e9;
d[s]=0;
//
for(int i=s;i>e;i--){
if(__builtin_popcount(i)!=n||d[i]==1e9)continue;//跳过不合法状态
int x=-1,y=0;
for(int j=n*2-1;j-1>=0;j--){
if(i>>j&1)x++;//维护坐标
else y++;
if((i>>j&1)&&!(i>>(j-1)&1)){//10拐点
d[i-(1<<(j-1))]=min(d[i-(1<<(j-1))],d[i]+(ch[x][y]!='.')*a[x][y]);//转移之后10变成01
//枚举另一个棋子
int nx=-1,ny=0;
for(int k=n*2-1;k-1>j;k--){
if(i>>k&1)nx++;
else ny++;
if((i>>k&1)&&!(i>>(k-1)&1)){//10拐点
if(ch[x][y]+ch[nx][ny]=='B'+'W'){
d[i-(1<<(j-1))-(1<<(k-1))]=min(d[i-(1<<(j-1))-(1<<(k-1))],d[i]+abs(a[x][y]-a[nx][ny]));//转移之后10变成01
}
}
}
//
}
}
printf("%d\n",d[e]);
}
return 0;
}