洛谷P3959 宝藏(状压dp)

传送门

 

为什么感觉状压dp都好玄学……FlashHu大佬太强啦……

设$f_{i,j}$表示当前选的点集为$i$,下一次要加入的点集为$j$时,新加入的点和原有的点之间的最小边权。具体的转移可以枚举$i$,然后枚举$i$的补集$j$,找出$j$的$lowbit_j$

那么转移就是$$f_{i,j}=min\{f_{i,j-lowbit_j}+min\{dis[lowbit_j][i]\}\}$$

据说这一部分的复杂度是$O(3^nn)$,因为$n$元素的所有子集的大小之和是$3^n$(然而我并不会证)

然后考虑把整张图给分层,设$g_{l,i}$表示总层数为$l$,已选点集为$i$时的最小答案,然后转移就是

$$g_{l,i}=\sum_{j\in i}g_{l-1,i-j}+l*f_{i-j,j}$$

据说这个复杂度也是$O(3^nn)$,最后$max\{g_{l,2^n-1}\}$就是答案了

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 6 char buf[1<<21],*p1=buf,*p2=buf;
 7 template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
 8 inline int read(){
 9     #define num ch-'0'
10     char ch;bool flag=0;int res;
11     while(!isdigit(ch=getc()))
12     (ch=='-')&&(flag=true);
13     for(res=num;isdigit(ch=getc());res=res*10+num);
14     (flag)&&(res=-res);
15     #undef num
16     return res;
17 }
18 const int N=13,M=4096,inf=0x01010101;
19 int a[N][N],f[M][M],g[N][M],ne[M],lg[M];
20 int n,m,S,s,l,x,y,v;
21 int main(){
22 //    freopen("testdata.in","r",stdin);
23     memset(a,1,sizeof(a));
24     memset(g,63,sizeof(g));
25     n=read(),m=read();
26     S=(1<<n)-1;
27     for(int i=0;i<n;++i) lg[1<<i]=i;
28     while(m--){
29         x=read(),y=read(),v=read();
30         if(a[--x][--y]>v) a[x][y]=a[y][x]=v;
31     }
32     for(int i=1;i<=S;++i){
33         v=0;
34         for(int j=s=S^i;j;j=(j-1)&s)
35         ne[j]=v,v=j;
36         //枚举i的补集,同时为了保证转移正确性要反着转移才行 
37         for(int j=v;j;j=ne[j]){
38             x=lg[j&-j],v=inf;
39             for(y=0;y<n;++y)
40             if(1<<y&i) cmin(v,a[x][y]);
41             f[i][j]=f[i][j^(j&-j)]+v;
42         }
43     }
44     for(int i=1;i<=S;i<<=1) g[0][i]=0;
45     for(int l=1;l<n;++l)
46     for(int i=1;i<=S;++i)
47     for(int j=i;j;j=(j-1)&i)
48     cmin(g[l][i],g[l-1][i^j]+f[i^j][j]*l);
49     int v=0x3f3f3f3f;
50     for(int l=0;l<=n;++l) cmin(v,g[l][S]);
51     printf("%d\n",v);
52     return 0;
53 }

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9683401.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值