• FZU 1977 Pandora adventure (插头DP)


    Problem 1977 Pandora adventure

    Accept: 246    Submit: 816
    Time Limit: 1000 mSec    Memory Limit : 32768 KB

    Problem Description

    The pollution of the earth is so serious that people can not survive any more. Fortunately, people have found a new planet that maybe has life, and we call it "Pandora Planet".

    Leonardo Da Vinci is the only astronaut on the earth. He will be sent to the Pandora Planet to gather some plant specimens and go back. The plant specimen is important to the people to decide whether the planet is fit to live or not.

    Assuming that Da Vinci can only move in an N×M grid. The positions of the plant specimens he wants to collect are all marked by the satellite. His task is to find a path to collect all the plant specimens and return to the spaceship. There are some savage beasts in the planet. Da Vinci can not investigate the grid with the savage beast. These grids are also marked by the satellite. In order to save time Da Vinci could only visit each grid exactly once and also return to the start grid, that is, you can not visit a grid twice except the start grid. You should note that you can choose any grid as the start grid.

    Now he wants to know the number of different paths he can collect all the plant specimens. We only care about the path and ignore where the start grid is, so the two paths in Figure 1 are considered as the same.

    Figure 1

    Input

    The first line of the input contains an integer T (T≤100), indicating the number of cases. Each case begins with a line containing two integers N and M (1≤N, M≤12), the size of the planet is N×M. Each of the following N lines contains M characters Gij(1≤i≤N, 1≤j≤M), Gij denotes the status of the grid in row i and column j, where 'X' denotes the grid with savage beast, '*' denotes the safe grid that you can decide to go or not, 'O' denotes the plant specimen you should collect. We guarantee that there are at least three plant specimens in the map.

    Output

    For each test case, print a line containing the test case number (beginning with 1) and the number of different paths he can collect all the plant specimens. You can make sure that the answer will fit in a 64-bit signed integer.

    Sample Input

    2 2 2 OO O* 4 4 ***O XO** **O* XX**

    Sample Output

    Case 1: 1 Case 2: 7

    Source

    The 35th ACM/ICPC Asia Regional Fuzhou Site —— Online Contest
     
    题目类型:插头DP。
    分析:很经典的插头DP题目。求回路数问题。但是此题是有的格子必须经过,有的格子不能经过,有的格子可以选择经过。
    这样导致回路最后一个位置不确定。
    所以每一个状态增加个标志位isend来记录是否形成回路。
    代码:
    /*
    FZU 1977
    简单回路数问题,N*M(1<=N,M<=12)的方格中有障碍点,必走点和非必走点
    因为回路只有一条,而形成回路的最后一个点又是不确定的。
    所以额外增加一个标志位来记录是否形成回路。
    如果已经形成回路,而后面又遇到插头或者必须走的点,则该方案不合法
    G++ 375ms
    */
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    using namespace std;
    
    const int MAXD=15;
    const int HASH=10007;
    const int STATE=100010;
    
    int N,M;
    int code[MAXD];
    int maze[MAXD][MAXD];//0表示障碍点,1表示非必走点,2表示必走点
    int ch[MAXD];
    int isend;//是否形成回路标记位
    
    struct HASHMAP
    {
        int head[HASH],next[STATE],size;
        long long state[STATE],f[STATE];
        void init()
        {
            size=0;
            memset(head,-1,sizeof(head));
        }
        void push(long long st,long long ans)
        {
            int i,h=st%HASH;
            for(i=head[h];i!=-1;i=next[i])
                if(state[i]==st)
                {
                    f[i]+=ans;
                    return;
                }
            state[size]=st;
            f[size]=ans;
            next[size]=head[h];
            head[h]=size++;
        }
    }hm[2];
    
    void decode(int *code,int m,long long st)//注意要增加一个isend标记位
    {
        for(int i=m;i>=0;i--)
        {
            code[i]=st&7;
            st>>=3;
        }
        isend=st&1;//isend标记位在st的最高一位
    }
    long long encode(int *code,int m)//增加isend标记位
    {
        long long st=isend;//最高位
    
        int cnt=1;
        memset(ch,-1,sizeof(ch));
        ch[0]=0;
        for(int i=0;i<=m;i++)
        {
            if(ch[code[i]]==-1)ch[code[i]]=cnt++;
            code[i]=ch[code[i]];
            st<<=3;
            st|=code[i];
        }
        return st;
    }
    void shift(int *code,int m)
    {
        for(int i=m;i>0;i--)code[i]=code[i-1];
        code[0]=0;
    }
    
    void dpblank(int i,int j,int cur)
    {
        int k,left,up;
        for(k=0;k<hm[cur].size;k++)
        {
            decode(code,M,hm[cur].state[k]);
            left=code[j-1];
            up=code[j];
            if(isend)
            {//如果已经形成环路,后面又有插头或者有必过点,则是非法的
                if(left||up||maze[i][j]==2)continue;
                code[j-1]=code[j]=0;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
                continue;
            }
            if(left&&up)
            {
                if(left==up)
                {
                    code[j-1]=code[j]=0;
                    isend=1;
                    if(j==M)shift(code,M);
                    hm[cur^1].push(encode(code,M),hm[cur].f[k]);
                }
                else //不在一个连通分量则合并
                {
                    code[j-1]=code[j]=0;
                    for(int t=0;t<=M;t++)
                      if(code[t]==up)
                        code[t]=left;
                    if(j==M)shift(code,M);
                    hm[cur^1].push(encode(code,M),hm[cur].f[k]);
                }
            }
            else if((left&&(!up))||((!left)&&up))
            {
                int t;
                if(left)t=left;
                else t=up;
                if(maze[i][j+1])
                {
                    code[j-1]=0;
                    code[j]=t;
                    hm[cur^1].push(encode(code,M),hm[cur].f[k]);
                }
                if(maze[i+1][j])
                {
                    code[j-1]=t;
                    code[j]=0;
                    if(j==M)shift(code,M);
                    hm[cur^1].push(encode(code,M),hm[cur].f[k]);
                }
            }
            else
            {
                if(maze[i][j+1]&&maze[i+1][j])
                {
                    code[j-1]=code[j]=13;
                    hm[cur^1].push(encode(code,M),hm[cur].f[k]);
                }
                if(maze[i][j]==1)//可以经过可以不经过的点
                {
                    code[j-1]=code[j]=0;
                    if(j==M)shift(code,M);
                    hm[cur^1].push(encode(code,M),hm[cur].f[k]);
                }
            }
        }
    }
    void dpblock(int i,int j,int cur)
    {
        int k;
        for(k=0;k<hm[cur].size;k++)
        {
            decode(code,M,hm[cur].state[k]);
            code[j-1]=code[j]=0;
            if(j==M)shift(code,M);
            hm[cur^1].push(encode(code,M),hm[cur].f[k]);
        }
    }
    char str[20];
    void init()
    {
        scanf("%d%d",&N,&M);
        memset(maze,0,sizeof(maze));
        for(int i=1;i<=N;i++)
        {
            scanf("%s",&str);
            for(int j=1;j<=M;j++)
            {
                if(str[j-1]=='*')maze[i][j]=1;
                else if(str[j-1]=='O')maze[i][j]=2;
            }
        }
    }
    void solve()
    {
        int i,j,cur=0;
        hm[cur].init();
        hm[cur].push(0,1);
        for(int i=1;i<=N;i++)
          for(int j=1;j<=M;j++)
          {
              hm[cur^1].init();
              if(maze[i][j])dpblank(i,j,cur);
              else dpblock(i,j,cur);
              cur^=1;
          }
        long long ans=0;
        for(i=0;i<hm[cur].size;i++)
          ans+=hm[cur].f[i];
        printf("%I64d\n",ans);
    }
    int main()
    {
       // freopen("in.txt","r",stdin);
       // freopen("out.txt","w",stdout);
        int T;
        int iCase=0;
        scanf("%d",&T);
        while(T--)
        {
            iCase++;
            printf("Case %d: ",iCase);
            init();
            solve();
        }
        return 0;
    }
    人一我百!人十我万!永不放弃~~~怀着自信的心,去追逐梦想
  • 相关阅读:
    poj_1836 动态规划
    动态规划——最长上升子序列
    poj_3260 动态规划
    poj_3628 动态规划
    动态规划——背包问题
    poj_2559 单调栈
    poj_3415 后缀数组+单调栈
    poj_2823 线段树
    poj_2823 单调队列
    poj_3250 单调栈
  • 原文地址:https://www.cnblogs.com/kuangbin/p/2709967.html
Copyright © 2020-2023  润新知