一直对状压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^{\frac{d}{2}}*n*(m+n)+2^d*n)$
最终枚举中间点,
这样做思想很重要
代码
#includeusing 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 "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<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<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< 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); }
v
题解
记忆化搜索+hash表
关于题目中说的编号右移,二进制下模拟一下就行了
st3=(st>>(l-p+1)<<(l-p))|((st&((1<<(l-p))-1)));
先右移再左移消掉这一位,然后后面保持原样
代码
#includeusing 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)); }