FZU 1977 Pandora adventure (插头dp)

来源:互联网 发布:淘宝运营费用 编辑:程序博客网 时间:2024/05/01 10:29

题意:

给出一个地图,包含三种格子的地图,'O'表示必须走的点,'*'表示可以走的点,'X'表示不能走到的点。问走完必须走的点的回路个数

题解:

这题很明显是单回路问题,但是格子有三种,因此就无法确定左后一个非障碍位置,因为我们引入一个变量标记是否某个状态是否已经形成回路。具体做法:每次将状态解压出来时都要让标记变量赋值为状态的最高位(最高位存的就是否是已经有回路的标记),每次在装以前要判断是否已经有回路,有回路要特殊判断转移方程。

#include<iostream>#include<math.h>#include<stdio.h>#include<algorithm>#include<string.h>#include<vector>#include<map>using namespace std;typedef __int64 lld;#define oo 0x3f3f3f3f#define OO 0x3f3f3f3f3f3f3f3f#define HASH 10007#define STATE 100010#define MAXD 15int N,M;int code[MAXD],maze[MAXD][MAXD];int ch[MAXD];int IsEnd;struct HASHMAP{    int head[HASH],next[STATE],sizes;    lld dp[STATE],state[STATE];    void init()    {        sizes=0;        memset(head,-1,sizeof head);    }    void push(lld st,lld ans)    {        int h=st%HASH;        for(int i=head[h];i!=-1;i=next[i])        {            if(st==state[i])            {                dp[i]+=ans;                return ;            }        }        dp[sizes]=ans;        state[sizes]=st;        next[sizes]=head[h];        head[h]=sizes++;    }}hm[2];void decode(int code[],int m,lld st){    for(int i=m;i>=0;i--)    {        code[i]=st&7;        st>>=3;    }    IsEnd=st&1;//相当于0——m中0的那位数组作为标记是否已经形成环}lld encode(int code[],int m)//最小表示法{    lld st=IsEnd;    int cnt=0;    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 left,up;    for(int k=0;k<hm[cur].sizes;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].dp[k]);            continue;        }        if(left&&up)///11 -> 00       有上插头和左插头,这种情况下相当于合并两个连通分量        {            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].dp[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].dp[k]);            }        }        else if(left||up)///01 || 10  上插头和左插头恰好有一个,这种情况相当于延续原来的连通分量        {            int temp;            if(left) temp=left;            else temp=up;            if(maze[i][j+1])            {                code[j-1]=0;                code[j]=temp;                hm[cur^1].push(encode(code,M),hm[cur].dp[k]);            }            if(maze[i+1][j])            {                code[j-1]=temp;                code[j]=0;                if(j==M)shift(code,M);///切记不可忘记,换行要shift                hm[cur^1].push(encode(code,M),hm[cur].dp[k]);            }        }        else///没有上插头和左插头,有下插头和右插头,相当于构成一个新的连通块        {            if(maze[i][j+1]&&maze[i+1][j])            {                code[j]=code[j-1]=13;                hm[cur^1].push(encode(code,M),hm[cur].dp[k]);            }            if(maze[i][j]==1)            {                code[j]=code[j-1]=0;                if(j==M)shift(code,M);                hm[cur^1].push(encode(code,M),hm[cur].dp[k]);            }        }    }}void dpblock(int i,int j,int cur){    for(int k=0;k<hm[cur].sizes;k++)    {        decode(code,M,hm[cur].state[k]);        code[j-1]=code[j]=0;///因为有障碍物所以 左插头j-1 和 上插头j 都消失了(就是不联通了)        if(j==M)shift(code,M);        hm[cur^1].push(encode(code,M),hm[cur].dp[k]);    }}void init(){    memset(maze,0,sizeof maze);    char c;    scanf("%d %d",&N,&M);    for(int i=1;i<=N;i++)    {        getchar();        for(int j=1;j<=M;j++)        {            scanf("%c",&c);            if(c=='*')                maze[i][j]=1;            else if(c=='O')                maze[i][j]=2;        }    }}void solve(){    int cur=0;    lld ans=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;        }    for(int i=0;i<hm[cur].sizes;i++)            ans+=hm[cur].dp[i];    printf("%I64d\n",ans);}int main(){    int T;    scanf("%d",&T);    for(int cas=1;cas<=T;cas++)    {        init();        printf("Case %d: ",cas);        solve();    }    return 0;}/**22 2OOO*4 4***OXO****O*XX***/





0 0