码迷,mamicode.com
首页 > 其他好文 > 详细

插头DP入门

时间:2019-08-04 15:29:23      阅读:207      评论:0      收藏:0      [点我收藏+]

标签:上下   isp   取出   sizeof   tchar   scanf   进制   event   opera   

  终于来补插头DP的坑了,咕了好久,主要是因为博猪代码实现能力太弱,而网上的大神们都只讲分类讨论。。。

  只放代码了:

  zzh学长:

技术图片
  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define ll long long
  4 #define A 1100000
  5 #define mod 299989
  6 #define P 8
  7 #define N 100000000
  8 ll n,m;
  9 inline ll find(ll state,ll id){
 10     return (state>>((id-1)<<1))&3;
 11 }//看当前插头究竟是什么插头
 12 //因为是四进制每两位代表一个状态
 13 struct bignum
 14 {
 15     ll n[10],l;
 16     bignum(){l=1,memset(n,0,sizeof(n));}
 17     void clear(){while(l>1&&!n[l-1]) l--;}
 18     void print(){
 19         printf("%lld",n[l-1]);
 20         for(ll i=l-2;i>=0;i--)
 21             printf("%0*lld",P,n[i]);
 22         printf("\n");
 23     }
 24     bignum operator +(bignum x)const{
 25         bignum t=*this;
 26         if(t.l<x.l) t.l=x.l;
 27         t.l++;
 28         for(ll i=0;i<t.l;i++){
 29             t.n[i]+=x.n[i];
 30             if(t.n[i]>=N){
 31                 t.n[i+1]+=t.n[i]/N;
 32                 t.n[i]%=N;
 33             }
 34         }
 35         t.clear();
 36         return t;
 37     }
 38     bignum operator =(ll x){
 39         l=0;
 40         while(x){
 41             n[l++]=x%N;
 42             x/=N;
 43         }
 44         return *this;
 45     }
 46     inline void operator +=(bignum b){*this=*this+b;}
 47 }Ans;
 48 struct hash_map
 49 {
 50     bignum val[mod];
 51     ll key[A],hash[mod],size;
 52     inline void init(){
 53         memset(val,0,sizeof(val));
 54         memset(key,-1,sizeof(key));size=0;
 55         memset(hash,0,sizeof(hash));
 56     }
 57     inline void newhash(ll id,ll v){hash[id]=++size;key[size]=v;}
 58     bignum &operator [] (const ll &state){
 59         for(ll i=state%mod;;i=(i+1==mod)?0:i+1)
 60         {
 61             if(!hash[i]) newhash(i,state);
 62             if(key[hash[i]]==state) return val[hash[i]];
 63         }
 64     }
 65 }f[2];
 66 inline void Set(ll &state,ll bit,ll val){
 67     bit=(bit-1)<<1;
 68     state|=3<<bit;
 69     state^=3<<bit;
 70     //把state高位先赋成0再把它赋成别的
 71     state|=val<<bit;
 72     //state表示状态
 73     //因为插头的编号为0--m所以bit要-1
 74     //因为是四进制,所以3<<
 75     //全都是4进制
 76     //2<<bit
 77     //1<<bit
 78     //貌似还能理解
 79     //每两位代表一个具体状态
 80 }
 81 ll link(ll state,ll pos){
 82     //找到对应的插头(用括号匹配的方式)然后
 83     ll cnt=0,delta=(find(state,pos)==1)?1:-1;
 84     //如果是左括号应该向右寻找右括号
 85     //如果是右括号应该向左寻找左括号
 86     for(ll i=pos;i&&i<=m+1;i+=delta)//一共m+1个插头
 87     {
 88         ll plug=find(state,i);
 89         if(plug==1)
 90             cnt++;//左括号数量++
 91         else if(plug==2)
 92             cnt--;//右括号数量++
 93         if(cnt==0)//当左括号数量与右括号数量相等时找到匹配
 94              return i;//找到了与当前插头对应的插头
 95     }
 96     return -1;
 97     //当前状态是非法的找不到与之对应的插头
 98 }
 99 inline void education(ll x,ll y){
100     ll now=((x-1)*m+y)&1,last=now^1,tot=f[last].size;
101     f[now].init();
102     for(ll i=1;i<=tot;i++){
103 //        printf("i=%lld\n",i);
104         ll state=f[last].key[i];//key状态
105         bignum Val=f[last].val[i];//取出上一次权值(方案数)
106         ll plug1=find(state,y),plug2=find(state,y+1);
107         //0--m编号,寻找轮廓线上编号y-1,y对应的插头
108         //至于为什么是y y+1,因为在上面函数里进行了减1
109         //编号为y-1是左右插头,y代表上下插头
110         if(link(state,y)==-1||link(state,y+1)==-1)
111             continue;
112         //当前括号无法找到匹配无解
113         if(!plug1&&!plug2){
114             if(x!=n&&y!=m){
115         //如果没有插头,直接拽过来两个插头相连(此题保证必须连通)
116                 Set(state,y,1);
117                 //在轮廓线上位置为y-1添加一个左括号
118                 Set(state,y+1,2);
119                 //y位置添加一个右括号
120                 f[now][state]+=Val;
121             }
122         }
123         else if(plug1&&!plug2){
124         //拥有左右插头没有上下插头
125         //两种转移方式,转弯向下走
126         //这样插头状态不变
127             if(x!=n)
128                 f[now][state]+=Val;
129         //向右连接一个插头
130         //向右推进要发生改变
131             if(y!=m){
132                 Set(state,y,0);
133                 Set(state,y+1,plug1);
134                 f[now][state]+=Val;
135             }
136         }
137         else if(!plug1&&plug2){
138         //拥有上下插头而没有左右插头
139         //两种转移方式,向右转移
140         //这样插头状态不变
141             if(y!=m)
142                 f[now][state]+=Val;
143         //向右连接一个插头
144             if(x!=n){
145                 Set(state,y,plug2);
146                 Set(state,y+1,0);
147                 //plug2是左右插头让上下方向转弯
148                 f[now][state]+=Val;
149             }
150         }
151         else if(plug1==1&&plug2==1){
152             //两个左括号插头相连接,然后让最靠左的右括号插头变成左括号插头
153             Set(state,link(state,y+1),1);
154             Set(state,y,0);
155             Set(state,y+1,0);
156             f[now][state]+=Val;
157         }
158         else if(plug1==1&&plug2==2){
159             //右插头是左括号插头,上插头是右括号插头,连在一起
160             //构成回路
161             if(x==n&&y==m)
162                 Ans+=Val;
163         }
164         else if(plug1==2&&plug2==1){
165             //无脑合并
166             Set(state,y,0);
167             Set(state,y+1,0);
168             f[now][state]+=Val;
169         }
170         else if(plug1==2&&plug2==2){
171             //当你有左右插头右括号插头,上下插头为右插头
172             //合并为1,将最靠右左插头变为右插头
173             Set(state,link(state,y),2);
174             Set(state,y,0);
175             Set(state,y+1,0);
176             f[now][state]+=Val;
177         }
178     }
179 }
180 int main(){
181     scanf("%lld%lld",&n,&m);
182     if(n==1||m==1){printf("1\n");return 0;}
183     if(m>n) swap(n,m);
184     f[0].init();f[0][0]=1;
185     for(ll i=1;i<=n;i++)
186     {
187         for(ll j=1;j<=m;j++)    
188             education(i,j);
189         if(i!=n){
190             ll now=(i*m)&1,tot=f[now].size;
191             for(ll j=1;j<=tot;j++)
192                 f[now].key[j]<<=2;
193         }
194     }
195     Ans+=Ans;
196     Ans.print();
197 }
zzn大神解读版

  另一位大神:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int LIM=300005,Has=299989;
int n,m,e1,e2,v,u;LL ans;
int mp[15][15],bin[15],t[2];LL w[2][LIM];
int head[300005],to[2][LIM],Next[LIM];//t:状态总数,w:该状态的方案总数,to:各种状态
void ins(int zt,LL num) {//卓越的哈希技术
    int tmp=zt%Has+1;
    for(int i=head[tmp];i;i=Next[i])
        if(to[u][i]==zt) {w[u][i]+=num;return;}
    Next[++t[u]]=head[tmp],head[tmp]=t[u];
    to[u][t[u]]=zt,w[u][t[u]]=num;
}
void work() {
    t[u]=1,w[u][1]=1,to[u][1]=0;
    for(int i=1;i<=n;++i) {
        for(int j=1;j<=t[u];++j) to[u][j]<<=2;//切换行了
        for(int j=1;j<=m;++j) {
            v=u,u^=1;
            memset(head,0,sizeof(head)),t[u]=0;
            for(int k=1;k<=t[v];++k) {
                int zt=to[v][k],b1=(zt>>(j*2-2))%4,b2=(zt>>(j*2))%4;//提取关键格子上的两段轮廓线状态
                LL num=w[v][k];
                if(!mp[i][j]) {if(!b1&&!b2) ins(zt,num);}
                else if(!b1&&!b2) {if(mp[i+1][j]&&mp[i][j+1]) ins(zt+bin[j-1]+2*bin[j],num);}
                else if(!b1&&b2) {
                    if(mp[i][j+1]) ins(zt,num);
                    if(mp[i+1][j]) ins(zt-bin[j]*b2+bin[j-1]*b2,num);
                }
                else if(b1&&!b2) {
                    if(mp[i][j+1]) ins(zt-bin[j-1]*b1+bin[j]*b1,num);
                    if(mp[i+1][j]) ins(zt,num);
                }
                else if(b1==1&&b2==1) {
                    int kl=1;
                    for(int ttt=j+1;ttt<=m;++ttt) {
                        if((zt>>(ttt*2))%4==1) ++kl;
                        if((zt>>(ttt*2))%4==2) --kl;
                        if(!kl) {ins(zt-bin[j]-bin[j-1]-bin[ttt],num);break;}
                    }
                }
                else if(b1==2&&b2==2) {
                    int kl=1;
                    for(int ttt=j-2;ttt>=0;--ttt) {
                        if((zt>>(ttt*2))%4==1) --kl;
                        if((zt>>(ttt*2))%4==2) ++kl;
                        if(!kl) {ins(zt+bin[ttt]-2*bin[j]-2*bin[j-1],num);break;}
                    }
                }
                else if(b1==2&&b2==1) ins(zt-2*bin[j-1]-bin[j],num);
                else if(i==e1&&j==e2) ans+=num;
            }
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j) {
        char ch=getchar();
        while(ch^‘*‘&&ch^‘.‘) ch=getchar();
        if(ch==‘.‘) mp[i][j]=1,e1=i,e2=j;
    }
    bin[0]=1;for(int i=1;i<=12;++i) bin[i]=bin[i-1]<<2;
    work(),printf("%lld\n",ans);
    return 0;
}

 

插头DP入门

标签:上下   isp   取出   sizeof   tchar   scanf   进制   event   opera   

原文地址:https://www.cnblogs.com/toot-wjh/p/11298150.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!