[bzoj1210][HNOI2004]邮递员【插头dp】

原创 2018年04月16日 21:55:00

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=1210
【题解】
  一道插头dp的入门题。
  粗略来说就是从上往下,从左往右一格一格dp,用状态压缩的方法记录与未搜索格子的连通性(最小表示法或括号法)。
  具体可见陈丹琦的:《基于连通性状态压缩的动态规划问题》
  时间复杂度O(NM22(M+1)) 实际远远不到。
  

/* --------------
    user Vanisher
    problem bzoj-1210
----------------*/
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
using namespace std;
int read(){
    int tmp=0, fh=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    return tmp*fh;
}
const int L=21, N=110,T=(1<<22);
struct INT{
    int num[N],len;
    void reget(){
        int i;
        for (i=1; i<=len||num[i]!=0; i++)
            num[i+1]+=num[i]/10, num[i]=num[i]%10;
        len=i-1;
    }
    void tonum(char *s){
        len=strlen(s+1);
        for (int i=1; i<=len; i++) num[len-i+1]=s[i]-'0';
    }
    void operator =(int tmp){
        memset(this->num,0,sizeof(this->num));
        this->len=0;
        while (tmp!=0) 
            this->num[++(this->len)]=tmp%10, tmp/=10;
    }
    INT operator +(INT b){
        INT c=*this; int i; c.len=max(c.len,b.len);
        for (i=1; i<=c.len; i++) c.num[i]=c.num[i]+b.num[i];
        c.reget(); return c;
    }
    INT operator *(INT b){
        INT c; c.len=b.len+len-1; int i,j;
        memset(c.num,0,sizeof(c.num));
        for (i=1; i<=len; i++)  
            for (j=1; j<=b.len; j++)
                c.num[i+j-1]=c.num[i+j-1]+num[i]*b.num[j];
        c.reget(); return c;
    }
    INT operator *(int tmp){ INT b; b=tmp; return ((*this)*b);}
    INT operator +(int tmp){ INT b; b=tmp; return ((*this)+b);}
    void read(){ char s[N]; scanf("%s",s+1); tonum(s); }
    void print(){
        if (len==0){ printf("0\n"); return; }
        for (int i=len; i>=1; i--) printf("%d",num[i]);
        printf("\n");
    }
}one,ans;
vector <INT> f[2];
vector <int> g[2];
int n,m,now[N],nex[N],f1,f2,lim,h[T],p[T],id;
int getnum(){
    int num=0;
    for (int i=0; i<m+1; i++)
        num=num+(nex[i]<<(i*2));
    return num;
} 
void join(INT num){
    int tmp=getnum();
    if (h[tmp]==-1){
        h[tmp]=f[f2].size();
        p[f[f2].size()]=tmp;
        f[f2].push_back(num);
        g[f2].push_back(tmp);
    }
    else f[f2][h[tmp]]=f[f2][h[tmp]]+num;
}
int main(){
    n=read(), m=read();
    if (n==1||m==1){
        printf("1\n");
        return 0;
    }
    if (n<m) swap(n,m);
    one=1;
    f1=0, f2=1, lim=(1<<((m+1)*2));
    f[f1].push_back(one); 
    g[f1].push_back(1+(2<<(1*2)));
    memset(h,-1,sizeof(h));
    for (int i=0; i<n; i++){
        for (int j=0; j<m; j++){
            if (i==0&&j==0) continue;
            for (unsigned k=0; k<g[f1].size(); k++) h[p[k]]=-1;
            for (unsigned k=0; k<g[f1].size(); k++){
                if (g[f1][k]>=lim) continue;
                INT num=f[f1][k];
                for (int t=0; t<m+1; t++)
                    now[t]=nex[t]=(g[f1][k]>>(t*2))&3;
                if (now[j]!=0&&now[j+1]!=0){
                    if (now[j]==1&&now[j+1]==2&&i==n-1&&j==m-1) 
                        if (g[f1][k]-(now[j]<<(j*2))-(now[j+1]<<(j*2+2))==0)
                            ans=ans+num;
                    if (now[j]==2&&now[j+1]==1) 
                        nex[j]=0, nex[j+1]=0, join(num);
                    if (now[j]==2&&now[j+1]==2){
                        nex[j]=0, nex[j+1]=0;
                        int t=j-1, cnt=0;
                        while (!(cnt==0&&now[t]==1)){
                            if (now[t]==1) cnt--;
                            if (now[t]==2) cnt++;
                            t--;
                        }
                        nex[t]=2; join(num);
                    }
                    if (now[j]==1&&now[j+1]==1){
                        nex[j]=0, nex[j+1]=0;
                        int t=j+2, cnt=0;
                        while (!(cnt==0&&now[t]==2)){
                            if (now[t]==2) cnt--;
                            if (now[t]==1) cnt++;
                            t++;
                        }
                        nex[t]=1; join(num);
                    }
                }
                if (now[j]==0&&now[j+1]!=0){
                    nex[j]=0, nex[j+1]=now[j+1]; join(num);
                    nex[j]=now[j+1], nex[j+1]=0; join(num);
                }
                if (now[j]!=0&&now[j+1]==0){
                    nex[j]=0, nex[j+1]=now[j]; join(num);
                    nex[j]=now[j], nex[j+1]=0; join(num);
                }
                if (now[j]==0&&now[j+1]==0){
                    nex[j]=1, nex[j+1]=2; join(num);
                }   
            }
            swap(f1,f2);
            g[f2].clear(); f[f2].clear();
        }
        for (unsigned k=0; k<g[f1].size(); k++) g[f1][k]=g[f1][k]<<2;
    }
    ans=ans*2;
    ans.print();
    return 0;
}

BZOJ 1210 HNOI 2004 邮递员 插头DP

题目有句话是不希望走更长的路,应该就是哈密尔顿回路了?然后总数乘个2? 还要注意n=1或m=1的时候并没有回路的情况。。 我太弱了,COGS上看到有人说76行即可AC,我写了个99行的。。 不过...
  • huanghongxun
  • huanghongxun
  • 2016-03-04 18:52:47
  • 821

[BZOJ]1210: [HNOI2004]邮递员 插头DP+高精度

插头DP+高精度
  • baidu_36797646
  • baidu_36797646
  • 2017-10-09 13:23:48
  • 126

BZOJ 1210: [HNOI2004]邮递员

写了两个小时。。。。。。。。 毒瘤三件套:插头DP,哈希判重,高精度 学(抄)到了插头DP的书写艺术 #include #include #include #include using name...
  • nlj1999
  • nlj1999
  • 2016-03-28 15:42:44
  • 626

插头dp初探:BZOJ1210&&Uva1519

插头dp初探,插头dp入门 bzoj1187 Uva1519 插头dp好题、必做题
  • qq_34025203
  • qq_34025203
  • 2016-03-12 15:39:57
  • 464

【HNOI2004】邮递员 (插头DP一条回路)

【HNOI2004】邮递员   Time Limit:10000MS  Memory Limit:65536K Total Submit:47 Accepted:21  Case Time L...
  • fp_hzq
  • fp_hzq
  • 2012-04-29 21:45:08
  • 1675

Vijos P1110 小胖邮递员(HNOI2004)题解

288行代码,恩…… 网上抄的代码,自己翻译,排版了一下,给大家学习。 主要方法是状态压缩dp,然后就是高精度,最后加一点特判。 主要,n或m为1时路径只有1条,而不是0条, 虽然是最短,但没...
  • Forest_of_Fir
  • Forest_of_Fir
  • 2015-05-08 20:42:58
  • 678

【BZOJ1210】【HNOI2004】邮递员

【题目链接】点击打开链接【思路要点】基于连通性的的动态规划模板题。时间复杂度\(O(3^M*N*M)\),实现时需要手写一个小高精。【代码】#include&amp;lt;bits/stdc++.h&...
  • qq_39972971
  • qq_39972971
  • 2018-04-17 18:37:24
  • 14

bzoj 1210 [HNOI2004] 邮递员 插头dp

插头dp板子题?? 搞了我一晚上,还tm全是抄的标程。。 还有高精,哈希混入,还是我比较弱,orz各种dalao 有不明白的可以去看原论文。。 #include #include #inclu...
  • Ren_Ivan
  • Ren_Ivan
  • 2017-08-08 17:38:16
  • 123

插头DP学习小结

插头dp一般可以解决一类需要记录联通状态的题目(比如一个棋盘求满足某某条件的路径等)。定义插头一个格子如果与它正上方的格子联通,则有一个上插头,下左右亦然。轮廓线我们解决问题的时候,一般是外循环从上到...
  • WorldWide_D
  • WorldWide_D
  • 2017-02-23 15:28:56
  • 1070

插头DP——从不会到入门(POJ 2411,HDU 1565,HDU 2167,HDU 1693,Ural 1519)

#include #include #include #include using namespace std; #define LL long long const int maxn=10...
  • kbdwo
  • kbdwo
  • 2013-11-10 17:01:02
  • 10403
收藏助手
不良信息举报
您举报文章:[bzoj1210][HNOI2004]邮递员【插头dp】
举报原因:
原因补充:

(最多只允许输入30个字)