jzoj 4016. 【雅礼联考DAY01】圈地为王 状压dp+bfs转移

Description

在 n 行 m 列的网格中,你要圈一些地。
你从左上角出发,最后返回左上角,路径内部的区域视为被你圈住。 你不可以进入网格内部, 只能在边上行走。 你的路径不能在左上角以外自交, 但是边足够宽, 你可以重复经过而不自交。
网格中有一些格子对你很重要,你要尽量圈住它;而另一些格子对你有坏处,你不能圈住它。
求圈住 i 个重要的格子的最小路径长度。

Input

n 行,每行 m 个字符。
‘I’表示重要的格子, ‘X’表示有坏处的格子, ‘.’表示其他格子。

Output

输出重要的格子数行, 第 i 行表示圈住 i 个重要的格子的最小路径长度。

Sample Input

X.I
.I.
I..

Sample Output

8
10
14

Data Constraint

jzoj 4016. 【雅礼联考DAY01】圈地为王 状压dp+bfs转移_第1张图片

分析:
一个点是包含在闭合图内,当其到图外一点有奇数个交点,也就是格点正上方有奇数条边。

因为我们在格点周围走,所以相当于走格点图的线边,可以看做一个横坐标为0~n,纵坐标为0~m的网格图。

我们可以用一个二进制数说明一个点上方边数的奇偶性。
我们设 f[x][y][s] f [ x ] [ y ] [ s ] 为在网格图上,走到第x行,第y列,特殊点奇偶性情况为s,最少走的边数。因为一种有交点的走法完全可以通过改变点的经过顺序来做到无交点,所以相当于随便走。


f[x+dx[i]][y+dy[i]][sub]=min(f[x][y][s]+1,f[x+dx[i]][y+dy[i]][sub]) f [ x + d x [ i ] ] [ y + d y [ i ] ] [ s u b ] = m i n ( f [ x ] [ y ] [ s ] + 1 , f [ x + d x [ i ] ] [ y + d y [ i ] ] [ s u b ] )

其中sub为走了后的状态,s为走之前的状态,可以发现,只有左右走才会改变状态。
bfs搜索转移就可以(类似于spfa)。答案就是 f[0][0][s] f [ 0 ] [ 0 ] [ s ] ,因为走到 (0,0) ( 0 , 0 ) ,就相当于走了一条闭合的回路,而 s s 中不包含点的状态都为0,其包含的重要点的1的个数就是回路包含的重要点数,相同点数取最值即可。初始值 f[0][0][0]=0 f [ 0 ] [ 0 ] [ 0 ] = 0 ,其他都为inf。

代码:

#include 
#include 
#include 
#include 
#include 

const int maxn=54;
const int inf=0x3f3f3f3f;
const int dx[4]={1,0,-1,0};
const int dy[4]={0,1,0,-1};

using namespace std;

int n,m,cnt1,cnt2;
int f[maxn][maxn][1050];
int ans[20];

struct node{
    int x,y,s;
};

struct rec{
    int x,y;
}imp[10],bad[10];

queue  q;

void init()
{
    char s[100];
    char ch;    
    while (scanf("%s",s)!=EOF)
    {
        n++;
        scanf("%c",ch);
        m=strlen(s);
        for (int j=1;j<=m;j++)
        {
            if (s[j-1]=='X') bad[++cnt2]=(rec){n,j};
            if (s[j-1]=='I') imp[++cnt1]=(rec){n,j};
        }
    }   
}

int calc(int x)
{
    int sum=0;
    while (x>0)
    {
        if (x%2) sum++;
        x/=2;
    }
    return sum;
}

int main()
{
    init();
    node c=(node){0,0,0};
    memset(f,inf,sizeof(f));
    memset(ans,inf,sizeof(ans));
    f[0][0][0]=0;
    q.push(c);

    while (!q.empty())
    {
        node u=q.front();
        q.pop();
        int x=u.x,y=u.y,s=u.s;
        for (int i=0;i<=3;i++)
        {
            int xx=x+dx[i],yy=y+dy[i];
            if ((xx>=0) && (yy>=0) && (xx<=n) && (yy<=m))
            {
                int sub=s;
                if (i==1)
                {
                    for (int j=1;j<=cnt1;j++)
                    {
                        if ((imp[j].y==y+1) && (imp[j].x>=x+1)) sub^=1<<(j-1);
                    }
                    for (int j=1;j<=cnt2;j++)
                    {
                        if ((bad[j].y==y+1) && (bad[j].x>=x+1)) sub^=1<<(cnt1+j-1);
                    }
                }
                if (i==3)
                {
                    for (int j=1;j<=cnt1;j++)
                    {
                        if ((imp[j].y==y) && (imp[j].x>=x+1)) sub^=1<<(j-1);
                    }
                    for (int j=1;j<=cnt2;j++)
                    {
                        if ((bad[j].y==y) && (bad[j].x>=x+1)) sub^=1<<(cnt1+j-1);
                    }
                }
                if (f[xx][yy][sub]>f[x][y][s]+1)
                {
                    f[xx][yy][sub]=f[x][y][s]+1;
                    node u=(node){xx,yy,sub};
                    q.push(u);
                }
            }
        }
    }
    for (int i=1;i<1<<(cnt1);i++)
    {
        int d=calc(i);
        ans[d]=min(ans[d],f[0][0][i]);
    }
    for (int i=1;i<=cnt1;i++) printf("%d\n",ans[i]);
}

你可能感兴趣的:(jzoj 4016. 【雅礼联考DAY01】圈地为王 状压dp+bfs转移)