URAL 1519 formula 1 插头dp

假设我们当前已经填充成如图所示的情况:需要填写(i,j)这个格子了
那么:我们有两个大类,六种情况~~~

第一类:
(i,j)格子本身是个障碍点,不可达

那么,应该是如图:


当左插头和上插头都不存在的时候,那么才是合法的状态,可以状态转移


第二类:
(i,j)可达

第一种情况:

左插头和上插头都存在,且ID号不同:说明需要合并连通分量(ID号相同合并也没有啥意义)
ID号相同且到达最后一个节点:
计算终态

第二种情况:

当只有左插头,没有上插头时候,我们可以发现,插头可以往右边走,也可以往下边走

同理,当只有上插头时,插头可以右走,也可以下走
这样,出现了四种状态转移

第三种情况:

当左右插头都没有时,因为这个点必须在回路上,所以要新生成一个连通分量,也是一种状态转移~~~

对着网上题解敲了两种~~~

第一种的注释配上图片应该还是蛮好理解的~(自我感觉良好)

#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

#define ll long long

const int STATE = 1000000 + 10;
const int HASH = 30007;
const int MAXD = 15;
int N,M;
int ex,ey;
int cur;
int mp[MAXD][MAXD];
ll sum;

struct HASHMAP{
    int Size,Head[HASH],Next[STATE];
    long long state[STATE];
    long long f[STATE];
    void init(){
        memset(Head,-1,sizeof(Head));
        Size=0;
    }
    void push(ll st,ll num){
        ll h = st % HASH;
        for(int i=Head[h];i!=-1;i=Next[i])
        if (state[i]==st){
            f[i]+=num;
            return;
        }
        Next[Size]=Head[h];
        Head[h]=Size;
        f[Size]=num;
        state[Size]=st;
        Size++;
    }
}hm[2];

void decode(int *code,ll st){
    for(int i=M;i>=0;i--){
        code[i]=st&7;
        st>>=3;
    }
}

ll encode(int *code){
    int ch[MAXD];
    memset(ch,-1,sizeof(ch));
    ch[0]=0;
    int cnt=1;
    ll st=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){
    for(int i=M;i>=1;i--)
        code[i]=code[i-1];
    code[0]=0;
}

//当前格子为障碍格子
void dpblock(int i,int j){
    for(int k=0;k<hm[cur].Size;k++){
        ll st=hm[cur].state[k];
        int code[MAXD];
        decode(code,st);
        int Left=code[j-1],Up=code[j];
        //没有插头,才能生成新状态
        //否则是非法状态
        if (Left==0 && Up==0){
            if (j==M) shift(code);
            hm[1-cur].push(encode(code),hm[cur].f[k]);
        }
    }
}

void dpblank(int i,int j){
    for(int k=0;k<hm[cur].Size;k++){
        ll st=hm[cur].state[k];
        ll num=hm[cur].f[k];
        int code[MAXD];
        decode(code,st);
        int Left=code[j-1],Up=code[j];
        if (Left>0 && Up>0){
            if (Left != Up){
                code[j-1] = code[j] = 0;
                for(int l=0;l<=M;l++)
                    if (code[l]==Up)
                        code[l]=Left;
                if (j==M) shift(code);
                hm[1-cur].push(encode(code),num);
            }
            //左插头和上插头相遇
            //到达终点了,(ex,ey)标记的是最后一个非可达格子
            else if (i==ex && j==ey){
                sum += num;
                code[j-1] = code[j] = 0;
                if (j==M) shift(code);
                hm[1-cur].push(encode(code),num);
            }
        }
        //左插头和下插头存在某一个
        //上面的情况已经判断了两个都存在的情况,所以这里不可能存在两个
        else if (Left > 0 || Up > 0){
            //(i,j)的右边可达,是个可达格子
            if (mp[i][j+1]){
                code[j-1]=0;
                //这里写的是加,因为有一个是0,所以表达的是延续这个插头状态
                code[j]=Left + Up;
                if (j==M) shift(code);
                hm[1-cur].push(encode(code),num);
            }
            if (mp[i+1][j]){
                code[j-1]=Left + Up;
                code[j]=0;
                if (j==M) shift(code);
                hm[1-cur].push(encode(code),num);
            }
        }
        else{
            //(i,j)格子没有左插头和上插头
            //(i,j)的下面和右边都是可达格子
            //否则,(i,j)不在回路中
            if (mp[i][j+1] && mp[i+1][j]){
                int maxc=1;
                for(int l=0;l<=M;l++)
                    maxc=max(maxc,code[l]);
                //新建一个连通分量
                code[j-1]=code[j]=maxc+1;
                hm[1-cur].push(encode(code),num);
            }
        }
    }
}

void init(){
    char a;
    ex=ey=0;
    memset(mp,0,sizeof(mp));
    for(int i=1;i<=N;i++){
        for(int j=1;j<=M;j++){
            scanf("%c",&a);
            if (a=='.'){
                mp[i][j]=1;
                ex=i;
                ey=j;
            }
        }
        getchar();
    }
}

void solve(){
    sum=0;
    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[1-cur].init();
            if (mp[i][j])
                dpblank(i,j);
            else
                dpblock(i,j);
            cur=1-cur;
        }
    //printf("%I64d\n",sum);
    printf("%lld\n",sum);
}

int main(){
    //freopen("input.txt","r",stdin);
    while(scanf("%d%d",&N,&M)!=EOF){
        getchar();
        init();
        if (ex==0){
            puts("0");
            continue;
        }
        solve();
    }
    return 0;
}


第二种写法来自我bin神的博客:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;

const int MAXD = 15;
const int HASH = 30007;
const int STATE = 1000000 + 10;

int N,M;
int mp[MAXD][MAXD];
int code[MAXD];
int ch[MAXD];
int ex,ey;

#define ll long long

struct HASHMAP{
    int Head[HASH],Next[HASH],Size;
    ll state[STATE];
    ll f[STATE];
    void init(){
        Size = 0;
        memset(Head,-1,sizeof(Head));
    }
    void push(ll st,ll ans){
        int h=st%HASH;
        for(int 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,ll st){
    for(int i=m;i>=0;i--){
        code[i]=st&7;
        st>>=3;
    }
}

ll encode(int *code,int m){
    int cnt=1;
    memset(ch,-1,sizeof(ch));
    ch[0]=0;
    ll st=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 Up,Left;
    for(int k=0;k<hm[cur].Size;k++){
        decode(code,M,hm[cur].state[k]);
        Left=code[j-1];
        Up=code[j];
        if (Left && Up){
            if (Left == Up){
                if (i==ex && j==ey){
                    code[j-1] = code[j] = 0;
                    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 > 0){
            if (mp[i][j+1]){
                code[j-1]=0;
                code[j]=Left + Up;
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
            if (mp[i+1][j]){
                code[j-1]=Left + Up;
                code[j]=0;
                if (j==M) shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
        }
        else{
            if (mp[i][j+1] && mp[i+1][j]){
                int maxc=1;
                for(int t=0;t<=M;t++)
                    maxc=max(maxc,code[t]);
                code[j-1]=code[j]=maxc+1;
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
        }
    }
}

void dpblock(int i,int j,int cur){
    for(int k=0;k<hm[cur].Size;k++){
        decode(code,M,hm[cur].state[k]);
        if (code[j-1]==0 && code[j]==0){
            if (j==M) shift(code,M);
            hm[cur^1].push(encode(code,M),hm[cur].f[k]);
        }
    }
}

char str[MAXD];
void init(){
    memset(mp,0,sizeof(mp));
    ex=0;
    for(int i=1;i<=N;i++){
        scanf("%s",str);
        for(int j=0;j<M;j++)
        if (str[j]=='.'){
            ex=i;
            ey=j+1;
            mp[i][j+1]=1;
        }
    }
}

void solve(){
    int cur=0;
    ll 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 (mp[i][j])
            dpblank(i,j,cur);
        else
            dpblock(i,j,cur);
        cur^=1;
    }
    for(int i=0;i<hm[cur].Size;i++)
        ans+=hm[cur].f[i];
    printf("%lld\n",ans);
}

int main(){
    //freopen("input.txt","r",stdin);
    while(scanf("%d%d",&N,&M)!=EOF){
        init();
        if (ex==0){
            puts("0");
            continue;
        }
        solve();
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/kevin66654/article/details/79982033