Connect(bzoj 1948)
Description
给定一个R*C大小的迷宫,其中R,C均为奇数 迷宫中坐标为两个奇数的点不能通过,称为障碍,迷宫中其他不能通过的点统称为墙壁 坐标为两个偶数的点可以通过,称为房间,迷宫中其他可通过的点称为走廊。迷宫有边界 迷宫中放置有若干个物品(偶数个),物品一定放置在房间中 问如何将这些物品配对可以使得所有物品对之间路径的总和最小,任两条路径不能相交,并求路径
Input
第一行输入为两个整数: R与C(5 ≤ R ≤ 25,5 ≤ C ≤ 80) 其中R为行数,C为列数,R,C均为奇数。 接下来的R行每行包括C个字符,每个字符都是’+’、’|’、’-‘、空格或’X’中的一个,分别表示障碍及两种墙壁、空格表示房间或可通过的走廊,X表示房间中的物品。
Output
输出的第一行是一个整数,即最小分值。
/* 插头DP 这道题看了一晚上了,还是有很多地方不理解,Orz~ */ #include<cstdio> #include<iostream> #include<cstring> using namespace std; int n,m,inf,cnt; int f[2][26][(1<<13)+10],blo[110][31][4]; char s1[31][110],s[110][31]; int dx[4]={0,-1,0,1}; int dy[4]={-1,0,1,0}; void upd(int &x,int y){x=min(x,y);} int trs(int x,int y){ x=(x>>2)<<1;x|=y&1; x|=((y&2)>>1)<<m; return x; } int main(){ scanf("%d%d",&n,&m);gets(s1[0]+1); for(int i=1;i<=n;i++) gets(s1[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) s[j][i]=s1[i][j],cnt+=(s1[i][j]=='X'); swap(n,m); for(int i=2;i<n;i+=2) for(int j=2;j<m;j+=2) for(int k=0;k<4;k++) blo[i>>1][j>>1][k]=(s[i+dy[k]][j+dx[k]]!=' '); n/=2;m/=2; memset(f[1],0x3f,sizeof(f[1])); inf=f[1][1][0];f[1][1][0]=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ if(j==m) memset(f[~i&1],0x3f,sizeof(f[~i&1])); for(int k=0;k<1<<m+1;k++){ if(f[i&1][j][k]==inf) continue; int *nex; if(j==m) nex=f[~i&1][1]; else nex=f[i&1][j+1]; int v1=blo[i][j][2],v2=blo[i][j][3],val=f[i&1][j][k]; if(s[i<<1][j<<1]=='X'){ if((k&3)==3) continue; if((k&3)==0){ if(!v2) upd(nex[trs(k,1)],val+1); if(!v1) upd(nex[trs(k,2)],val+1); } else upd(nex[trs(k,0)],val); } else{ if((k&3)==3) upd(nex[trs(k,0)],val+1); else if((k&3)==0){ upd(nex[trs(k,0)],val); if(!v1&&!v2) upd(nex[trs(k,3)],val+3); } else { if(!v2) upd(nex[trs(k,1)],val+2); if(!v1) upd(nex[trs(k,2)],val+2); } } } } printf("%d\n",f[(n+1)&1][1][0]+cnt/2); return 0; }