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;
}

 

posted @ 2017-03-16 22:09  karles~  阅读(237)  评论(0编辑  收藏  举报