zoukankan      html  css  js  c++  java
  • [POJ 1739] Tony's Tour

    Link:

    POJ 1739 传送门

    Solution:

    这题除了一开始的预处理,基本上就是插头$dp$的模板题了

    由于插头$dp$求的是$Hamilton$回路,而此题有起点和终点的限制

    于是可以构造一条$[n,1]->[n+2,1]->[n+2,m]->[n,m]$的路径,正好只添加一条$S->T$的路径

    接下来就是插头$dp$的模板了

    推荐三篇文章,看完基本上就懂插头$dp$了吧,

    litble:https://blog.csdn.net/litble/article/details/79369147

    yhzq:远航之曲博客

    陈丹琦论文:http://www.doc88.com/p-9009338580746.html

    可以发现,插头$dp$其实就是对于当前已枚举部分和未枚举部分的轮廓线的状压$dp$

    注意每枚举过一行要将所有状态左移一位(下一行会多出来一个状态位)

    Code:

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <utility>
    #include <vector>
    
    using namespace std;
    typedef long long ll;
    
    const int INF=0x3f3f3f3f,MAXN=15,MAX_State=3e5+10,MAX_Hash=3e5;
    int n,m,pre,cur,dat[MAXN][MAXN];
    int st[2][MAX_State],st_cnt[2],bit[20];
    ll res[2][MAX_State],sum=0;
    
    struct edge{int to,next;}e[MAX_State];
    int hs[MAX_State],hs_cnt=0;
    void ins(int now,ll x)
    {
        int p=now%MAX_Hash;
        for(int i=hs[p];i;i=e[i].next) //Hash时最好使用链式前向星
            if(st[cur][e[i].to]==now){res[cur][e[i].to]+=x;return;}
        st_cnt[cur]++;
        
        e[++hs_cnt].to=st_cnt[cur];
        e[hs_cnt].next=hs[p];
        hs[p]=hs_cnt;
        
        st[cur][st_cnt[cur]]=now;res[cur][st_cnt[cur]]=x;
    }
    
    void plugDP()
    {
        sum=st[cur][1]=cur=0; //注意初始化的顺序
        st_cnt[cur]=res[cur][1]=1;
        
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=st_cnt[cur];j++) //左移一位
                st[cur][j]<<=2;
            for(int j=1;j<=m;j++)
            {
                hs_cnt=0;memset(hs,0,sizeof(hs));
                pre=cur;cur^=1;st_cnt[cur]=0;
                for(int k=1;k<=st_cnt[pre];k++)
                {
                    int now=st[pre][k];ll x=res[pre][k];
                    ll dw=(now>>bit[j-1])&3,rt=(now>>bit[j])&3;
                    ll numd=1<<bit[j-1],numr=1<<bit[j];
                    
                    if(!dat[i][j] && !dw && !rt) ins(now,x);
                    else if(!dw && !rt && dat[i+1][j] && dat[i][j+1])
                        ins(now+numd+2*numr,x);
                    else if(!dw && rt)
                    {
                        if(dat[i][j+1]) ins(now,x);
                        if(dat[i+1][j]) ins(now-rt*numr+rt*numd,x);
                    }
                    else if(dw && !rt)
                    {
                        if(dat[i+1][j]) ins(now,x);
                        if(dat[i][j+1]) ins(now-dw*numd+dw*numr,x);
                    }
                    else if(dw==1 && rt==1)
                    {
                        int flag=1;
                        for(int l=j+1;l<=m;l++)
                        {
                            if(((now>>bit[l])&3)==1) flag++;
                            if(((now>>bit[l])&3)==2) flag--;
                            if(!flag){ins(now-numd-numr-(1<<bit[l]),x);break;}
                        }
                    }
                    else if(dw==2 && rt==2)
                    {
                        int flag=-1;
                        for(int l=j-2;l>=0;l--)
                        {
                            if(((now>>bit[l])&3)==1) flag++;
                            if(((now>>bit[l])&3)==2) flag--;
                            if(!flag){ins(now-2*numd-2*numr+(1<<bit[l]),x);break;}
                        }                    
                    }
                    else if(dw==2 && rt==1) ins(now-2*numd-numr,x);
                    else if(dw==1 && rt==2 && i==n && j==m) sum+=x; 
                }
            }
        }
    }
    
    int main()
    {
        for (int i=1;i<15;i++) 
            bit[i]=i<<1;    
        while(~scanf("%d%d",&n,&m) && n && m)
        {
            memset(dat,0,sizeof(dat));
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                {
                    char ch=getchar();
                    while(ch!='.' && ch!='#') ch=getchar();
                    if(ch=='.') dat[i][j]=1;
                }
            n+=2;dat[n-1][1]=dat[n-1][m]=1; //预处理
            for(int i=1;i<=m;i++) dat[n][i]=1;
                    
            plugDP();printf("%lld
    ",sum);
        }
        return 0;
    }

    Review:

    做的时候犯的丝帛错误:

    1、对变量初始化的先后顺序要注意!!!

    EX:$cur=0$要在$st[cur][1]$之前初始化

    2、哈希表最好用链式前向星实现

    用$vector$时TLE了,可能是$vector.clear()$的时间太长了?

    链式前向星的一大优点就是重复使用时不用清空数组,只要$tot=0$即可

    3、左移、右移比$==$优先级高,但位与、位或比$==$优先级低!!

  • 相关阅读:
    MySQL灾备切换
    crontab 定时任务
    Mysql常用命令 详细整理版
    linux 常用命令
    shell逻辑运算总结, 包括[[]]与[]的区别,&&与-a的区别,||与-o的区别
    linux端口详解大全
    编译安装php5.6
    linux给用户添加sudo权限
    Effective C#(二)
    Effective C#(一)
  • 原文地址:https://www.cnblogs.com/newera/p/9141667.html
Copyright © 2011-2022 走看看