這題斯坦納樹的做法詳見最小斯坦納樹初探,此處寫插頭dp的做法。
用最小表示法來描述輪廓線上的連通性(如果某個點不選,記標號為0),與“括號表示法”不同,此輪廓線長度位m而非m+1,即輪廓線裡不在有當前格子左側的邊上的插頭(與左邊格子的插頭本質相同)。
設當前格子為(x,y),左側格子上的插頭p1,當前上方格子的插頭p2。顯然僅當(x,y)是不是景點且不選這個格子不會使得p2(如果有)脫離輪廓線時,(x,y)可以不選。(因為要保證最終所選的點聯通)
就醬。
#include <bits/stdc++.h>
const int inf=0x3f3f3f3f;
struct hash_map {
static const int P=23333;
int siz,hsh[P],key[P],val[P];
void clear() {
siz=0;
memset(hsh,0,sizeof hsh);
memset(key,-1,sizeof key);
memset(val,0x3f,sizeof val);
}
void new_hsh(int id,int sta) {
hsh[id]=++siz,key[siz]=sta;
}
int get(int sta) {
for(int i=sta%P; ; i=(i+1==P?0:i+1)) {
if(!hsh[i]) new_hsh(i,sta);
if(key[hsh[i]]==sta) return hsh[i];
}
}
} f[2];
int n,m;
int a[11][11],edx,edy;
bool slc[11][11];
int find(int sta,int bit) {
if(bit==0) return 0;
return (sta>>(3*(bit-1)))&7;
}
void set(int&sta,int bit,int val) {
bit=3*(bit-1);
sta|=7<<bit;
sta^=7<<bit;
sta|=val<<bit;
}
int cnt(int sta,int val) {
int c=0;
for(int i=1; i<=m; ++i,sta>>=3)
c+=(sta&7)==val;
return c;
}
int relabel(int sta) {
static int hs,cnt,w[11],id[11];
memset(id,-1,sizeof id);
hs=cnt=id[0]=0;
for(int i=1; i<=m; ++i,sta>>=3) w[i]=sta&7;
for(int i=m; i; --i) {
if(id[w[i]]==-1) id[w[i]]=++cnt;
hs=hs<<3|id[w[i]];
}
return hs;
}
bool unicom(int sta) { //存再標號>1 不只一個連通塊
for(int i=1; i<=m; ++i,sta>>=3)
if((sta&7)>1) return 0;
return 1;
}
struct state { //輸出方案
int x,y,last;
bool select;
} Q[10*10*23333];
int bas,lbas,cur,ans=inf,now,lst=1;
void p_dp(int x,int y) {
now=lst,lst^=1;
f[now].clear();
lbas=bas, bas+=f[lst].siz;
for(int i=1; i<=f[lst].siz; ++i) {
int sta=f[lst].key[i];
int val=f[lst].val[i];
int p1=find(sta,y-1);
int p2=find(sta,y);
if(a[x][y]&&(!p2||cnt(sta,p2)>1)) {
int tmp=sta;
set(tmp,y,0);
int c=f[now].get(tmp);
if(f[now].val[c]>val) {
f[now].val[c]=val;
Q[bas+c]=(state){x,y,lbas+i,false};
}
}
if(!p1&&!p2) set(sta,y,7);
else if(!p1&&p2);
else if(p1&&!p2) set(sta,y,p1);
else if(p1!=p2) {
for(int tmp=sta,i=1; i<=m; ++i,tmp>>=3)
if((tmp&7)==p1) set(sta,i,p2);
}
int c=f[now].get(relabel(sta));
if(f[now].val[c]>val+a[x][y]) {
f[now].val[c]=val+a[x][y];
Q[bas+c]=(state){x,y,lbas+i,true};
}
}
if(x<edx||(x==edx&&y<edy)) return;
for(int i=1; i<=f[now].siz; ++i) {
if(f[now].val[i]<ans&&unicom(f[now].key[i]))
ans=f[now].val[i],cur=bas+i;
}
}
int main() {
scanf("%d%d",&n,&m);
int tot=0;
for(int i=1; i<=n; ++i) {
for(int j=1; j<=m; ++j) {
scanf("%d",&a[i][j]);
if(!a[i][j]) edx=i,edy=j,tot++;
}
}
if(tot<2) return puts("0"),0;
f[now].clear();
f[now].val[f[now].get(0)]=0;
for(int i=1; i<=n; ++i) {
for(int j=1; j<=m; ++j) p_dp(i,j);
}
printf("%d
",ans);
for(int i=cur; i; i=Q[i].last) {
if(Q[i].select) slc[Q[i].x][Q[i].y]=1;
}
for(int i=1; i<=n; ++i,puts("")) {
for(int j=1; j<=m; ++j) {
if(!a[i][j]) putchar(`x`);
else if(slc[i][j]) putchar(`o`);
else putchar(`_`);
}
}
return 0;
}