POJ-1739:Tony's Tour

来源:互联网 发布:淘宝买家怎么提升等级 编辑:程序博客网 时间:2024/06/08 16:31

Tony's Tour
Time Limit: 1000MS Memory Limit: 30000KTotal Submissions: 4217 Accepted: 1967

Description

A square township has been divided up into n*m(n rows and m columns) square plots (1<=N,M<=8),some of them are blocked, others are unblocked. The Farm is located in the lower left plot and the Market is located in the lower right plot. Tony takes her tour of the township going from Farm to Market by walking through every unblocked plot exactly once. 
Write a program that will count how many unique tours Betsy can take in going from Farm to Market. 

Input

The input contains several test cases. The first line of each test case contain two integer numbers n,m, denoting the number of rows and columns of the farm. The following n lines each contains m characters, describe the farm. A '#' means a blocked square, a '.' means a unblocked square. 
The last test case is followed by two zeros. 

Output

For each test case output the answer on a single line.

Sample Input

2 2....2 3#.....3 4............0 0

Sample Output

114

思路:插头DP。这算是真正的入门题了吧。真的没想到DP里面也有这么长的代码。。。

这是第一次写这样的插头DP,状态转移有点复杂。主要参考了http://blog.csdn.net/xingyeyongheng/article/details/24415517

不过还没了解什么是插头DP的话,建议先仔细认真的看陈丹琦的《基于连通性状态压缩的动态规划问题》https://wenku.baidu.com/view/a6dce6c76137ee06eff918d1.html


#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>using namespace std;const int MAX=1000+10;    //开大了会TLEint head[MAX],nex[MAX],hash[MAX],size;  //hash链表long long d[2][MAX];   //二维滚动数组long long tp[2][MAX];  //记录状态long long total[2];    //状态总数量int ex,ey;   //记录最后一个非'#'的格子int n,m;int now,pre;char s[15][15];void insert(int x,long long sum)   //插入x这个状态{    int pos=x%MAX;    for(int i=head[pos];i!=-1;i=nex[i])    {        if(x==tp[now][hash[i]])    //找到相同的状态        {            d[now][hash[i]]+=sum;            return;        }    }    total[now]++;               //没找到相同状态就插入这个状态    tp[now][total[now]]=x;    d[now][total[now]]=sum;    hash[size]=total[now];    nex[size]=head[pos];    head[pos]=size++;}int main(){    while(cin>>n>>m&&(n||m))    {        for(int i=0;i<n;i++)scanf("%s",s[i]);        s[n][0]=s[n][m-1]='.';        for(int j=1;j<m-1;j++)s[n][j]='#';        n++;                                      //这里额外的加了2行以形成回路        for(int j=0;j<m;j++)s[n][j]='.';          //    .*****.        n++;                                      //    .......        for(int i=0;i<n;i++)        {            for(int j=0;j<m;j++)if(s[i][j]=='.')ex=i,ey=j;        }        memset(d,0,sizeof d);        memset(head,-1,sizeof head);        memset(total,0,sizeof total);        size=0;        pre=0,now=1;        d[0][1]=1;        tp[0][1]=0;        total[0]=1;        long long ans=0;        for(int i=0;i<n;i++)        {            for(int j=0;j<m;j++)            {                for(int k=1;k<=total[pre];k++)                {                    int state=tp[pre][k];       //取出状态                    int left=state&3;                        //左插头状况(0表示没有插头,1表示右括号插头,2表示左括号插头)                    int up=state&((1<<(2*m+1))+(1<<(2*m)));  //上插头状况(0表示没有插头,1<<(2*m)表示右括号插头,1<<(2*m+1)表示左括号插头)                    if(s[i][j]=='#')                         //当前位置不能通过                    {                        if(left==0&&up==0)insert(state<<2,d[pre][k]);                        continue;                    }                    if(left==0&&up==0)//左边上边都没插头                    {                        if(i+1>=n||j+1>=m||s[i+1][j]=='#'||s[i][j+1]=='#')continue;                        insert((state<<2)^9,d[pre][k]);        //新加入一个连通分量                    }                    if(left==0&&up)  //左边没有插头,上边有                    {                        if(j+1<m&&s[i][j+1]=='.')    //向右延伸插头                        {                            if(up&(1<<(2*m)))insert((state<<2)^(up<<2)^1,d[pre][k]);     //up为右括号                            if(up&(1<<(2*m+1)))insert((state<<2)^(up<<2)^2,d[pre][k]);   //up为左括号                        }                        if(i+1<n&&s[i+1][j]=='.')    //向下延伸插头                        {                            if(up&(1<<(2*m)))insert((state<<2)^(up<<2)^4,d[pre][k]);     //up为右括号                            if(up&(1<<(2*m+1)))insert((state<<2)^(up<<2)^8,d[pre][k]);   //up为左括号                        }                    }                    if(left&&up==0) //上边没有插头,左边有                    {                        if(j+1<m&&s[i][j+1]=='.')    //向右延伸插头                        {                            if(left&1)insert((state<<2)^(left<<2)^1,d[pre][k]);     //left为右括号                            if(left&2)insert((state<<2)^(left<<2)^2,d[pre][k]);     //left为左括号                        }                        if(i+1<n&&s[i+1][j]=='.')    //向下延伸插头                        {                            if(left&1)insert((state<<2)^(left<<2)^4,d[pre][k]);     //lfet为右括号                            if(left&2)insert((state<<2)^(left<<2)^8,d[pre][k]);     //left为左括号                        }                    }                    if(left&&up)   //左方上方都有插头                    {                        if(left==1&&(up&(1<<(2*m+1))))         //left为右括号,up为左括号                        {                            insert((state<<2)^(left<<2)^(up<<2),d[pre][k]);                        }                        if(left==1&&(up&(1<<(2*m))))           //left为右括号,up为右括号                        {                            for(int sum=1,t=j-1,l=2;t>=0;l+=2,t--)//往左找最近的一个左括号                            {                                if((2<<l)&state)sum--;                                if((1<<l)&state)sum++;                                if(sum==0)        //找到了,将其改为右括号                                {                                    insert((state<<2)^(left<<2)^(up<<2)^(3<<(l+2)),d[pre][k]);                                    break;                                }                            }                        }                        if(left==2&&(up&(1<<(2*m))))           //left为左括号,up为右括号                        {                            if(i==ex&&j==ey)  //只有最后一个格子才能合并形成回路                            {                                ans=d[pre][k];                            }                        }                        if(left==2&&(up&(1<<(2*m+1))))           //left为左括号,up为左括号                        {                            for(int sum=1,t=j+1,l=2;t<m;l+=2,t++)//往右找最近的一个右括号                            {                                if(( (1<<(2*m+1)) >>l)&state)sum++;                                if(( (1<<(2*m))   >>l)&state)sum--;                                if(sum==0)        //找到了,将其改为左括号                                {                                    insert((state<<2)^(left<<2)^(up<<2)^(((1<<(2*m+1))+(1<<(2*m)))>>(l-2)),d[pre][k]);                                    break;                                }                            }                        }                    }                }                memset(d[pre],0,sizeof d[pre]);                total[pre]=0;                now^=1;                pre^=1;                memset(head,-1,sizeof head);                size=0;            }        }        cout<<ans<<endl;    }    return 0;}