v·y「状压dp」

Posted znsbc-13

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了v·y「状压dp」相关的知识,希望对你有一定的参考价值。

一直对状压dp怀有一种恐惧感

不会打,不会调,关键是不会调

做了这两道题,虽然还是不会状压dp,但总比之前好了一些

y

技术图片

 

 普通状压应该很好打

复杂度$O(2^d*n*(n+m))$

    for(ll i=2;i<=d;i++)
        for(ll state=0;state<=maxn;state++)
            for(ll x=1;x<=n;x++)
                for(ll j=head[x];j;j=nxt[j])
                    ll y=ver[j];
                    if(!f[i-1][x][state]) continue ;
                    f[i][y][(state<<1)|edg[j]]|=f[i-1][x][state];
                
            
        
    

那么该怎么优化,

折半搜索,你起点是确定的,枚举中间点,这样复杂度就降低成$O(2^\\fracd2*n*(m+n)+2^d*n)$

最终枚举中间点,

这样做思想很重要

代码

技术图片
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 10101010
bool f1[21][93][1<<12],f2[21][93][1<<12],hash_map[1<<22];
ll nxt[A],ver[A],head[A],edg[A];
ll n,m,q,ans,tot,d;
void add(ll x,ll y,ll z)
    nxt[++tot]=head[x],head[x]=tot,ver[tot]=y,edg[tot]=z;

void turn(ll x,ll n)

     ll t=x,num=0,xx[100];
     while(x) xx[num++]=x%2,x/=2;    
     for(ll i=num;i<n;i++)printf("0");
     for(ll i=num-1;i>=0;i--)cout<<xx[i];
 //    puts("");

int main()
    scanf("%lld%lld%lld",&n,&m,&d);
    for(ll i=1,a,b,c;i<=m;i++)
        scanf("%lld%lld%lld",&a,&b,&c);
        add(a,b,c);add(b,a,c);
    
    for(ll i=head[1];i;i=nxt[i])
        ll y=ver[i];
        f1[1][y][edg[i]]=1;
    
    ll d1=d/2,d2=d-d1;
    ll maxn1=(1<<(d1))-1,maxn2=(1<<(d2))-1;
//    printf("d1=%lld d2=%lld\\n",d1,d2);
    for(ll i=2;i<=d1;i++)
        for(ll state=0;state<=((1<<i)-1);state++)
            for(ll x=1;x<=n;x++)
                for(ll j=head[x];j;j=nxt[j])
                    ll y=ver[j];
                    if(!f1[i-1][x][state]) continue ;
                    f1[i][y][(state<<1)|edg[j]]|=f1[i-1][x][state];
                
            
        
    
    for(ll i=1;i<=n;i++)
        for(ll j=head[i];j;j=nxt[j])
            ll y=ver[j];
            f2[1][y][edg[j]]=1;
        
    
//    printf("f2[1][1][1]=%lld\\n",1ll*f2[1][1][1]);
    for(ll i=2;i<=d2;i++)
        for(ll state=0;state<=((1<<i)-1);state++)
            for(ll x=1;x<=n;x++)
                for(ll j=head[x];j;j=nxt[j])
                    ll y=ver[j];
                    if(!f2[i-1][x][state]) continue ;
                    
//                    printf("f2[%lld][%lld][%lld]=%lld\\n",i-1,x,state,1ll*f2[i-1][x][state]);
                    f2[i][y][(state<<1)|edg[j]]|=f2[i-1][x][state];
//                    printf("f2now[%lld][%lld][%lld]=1\\n",i,y,(state<<1)|edg[j]);
                
            
        
    
    for(ll state=0;state<=maxn1;state++)
        for(ll state1=0;state1<=maxn2;state1++)
            for(ll i=1;i<=n;i++)
//                printf("f1[%lld][%lld][%lld]=%lld f2[%lld][%lld][%lld]=%lld state1=%lld\\n",d1,i,state,1ll*f1[d1][i][state],d2,i,state1,1ll*f2[d2][i][state1],maxn2);
                if(f1[d1][i][state]&&f2[d2][i][state1])
                    ll sum=state<<d2|state1;
//                    printf("***** i=%lld state=%lld state2=%lld\\n",i,state,state1);
//                    turn(sum,1);
//                    printf("\\n");
                    if(!hash_map[sum])
                        hash_map[sum]=1,ans++;
                
        
    
    printf("%lld\\n",ans);
View Code

v

技术图片

 

 

题解

记忆化搜索+hash表

关于题目中说的编号右移,二进制下模拟一下就行了

        st3=(st>>(l-p+1)<<(l-p))|((st&((1<<(l-p))-1)));

先右移再左移消掉这一位,然后后面保持原样

代码

技术图片
#include<bits/stdc++.h>
using namespace std;
#define ll int
#define mod 19260817
struct hash_map
    ll head[mod],nxt[mod],ver[mod];
    ll cnt;
    short len,L[mod];
    double val[mod];
    double &operator [] (const ll & st)
        int x=1ll*st*len%mod;
        for(ll i=head[x];i;i=nxt[i])
            if(ver[i]==st&&L[i]==len)
                return val[i];
        nxt[++cnt]=head[x],head[x]=cnt,ver[cnt]=st,L[cnt]=len,val[cnt]=-1;
        return val[cnt];
    
f;
ll n,k;
char c[33];
double dfs(ll l,ll st)
    if(l==n-k) return 0;
    f.len=l;
    if(f[st]>-0.5) return f[st];
    ll lst[33];
    ll rst=st;
    f[st]=0;
    for(ll i=1;i<=l;i++) lst[i]=rst&1,rst>>=1;
    for(ll i=1;i<=l/2;i++)
        ll j=l-i+1,st1=(st>>(l-i+1)<<(l-i))|(st&((1<<(l-i))-1)),st2=(st>>(l-j+1)<<(l-j))|(st&((1<<(l-j))-1));
        double ans1=dfs(l-1,st1)+lst[j],ans2=dfs(l-1,st2)+lst[i];
        f.len=l;f[st]+=2.0/((double)l)*max(ans1,ans2);
    
    if(l&1)
        ll p=l/2+1,st3=(st>>(l-p+1)<<(l-p))|((st&((1<<(l-p))-1)));
        double ans3=dfs(l-1,st3)+lst[p];
        f.len=l;f[st]+=1.0/l*ans3;
    
    return f[st];

int main()
    scanf("%d%d",&n,&k);
    scanf("%s",c+1);
    ll st=0,cnt=0;
    for(ll i=1;i<=n;i++)
        st=((st<<1)|(c[i]==W));
        if(c[i]==W) cnt++;
    
    if(k==n)
        printf("%d\\n",cnt);
        return 0;
    
    printf("%.8lf\\n",dfs(n,st));
View Code

 

以上是关于v·y「状压dp」的主要内容,如果未能解决你的问题,请参考以下文章

[转]状态压缩dp(状压dp)

状压dp入门

HDU-3811 Permutation 状压DP

状压dp常用操作

状压dp中常用的位运算式子

题解[SCOI2005] 互不侵犯 (状压DP)