暑假集训随笔3 dp进阶2
状压dp
本身没啥可说的,这玩意主打一个技巧多。
技巧1
下面是一个用于枚举某个二进制数所表示集合的子集的二进制形式的代码。
//S为二进制数
for(int x=S;x;x=S&(x-1))
cout<<x<<" ";
技巧2
用一切方式避免直接进行严格
#include<bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<utility>
#include<string.h>
#include<queue>
#include<stack>
using namespace std;
#define int long long
#define dnt double
#define rep(i,j,k) for(int i=(j);i<=(k);++i)
#define dow(i,j,k) for(int i=(j);i>=(k);--i)
#define pr pair
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
//int gp() {int x;while((x=rand())<=0)srand(time(0));return x%1000;}
inline int read(int &x) {
x=0;int ff=1;char ch=getchar();
while (ch<'0'||ch>'9') {
if (ch=='-') ff=-1;ch=getchar();
}
while (ch>='0'&&ch<='9') {
x=x*10+ch-48;ch=getchar();
}
return x*ff;
}
void write(int x) {
if (x > 9)write(x / 10);
putchar((x % 10) + '0');
}
inline int max(int a,int b) {return a>b?a:b;}
inline int min(int a,int b) {return a>b?b:a;}
inline int exgcd(int a,int b,int&x,int&y) {
if(b==0) {x=1,y=0 ;return a ;}
else {int r=exgcd(b,a%b,x,y);int t=x ;x=y ;y=t-a/b*y ;return r ;}
}
const int N=1000005;
int fpow(int x,int y) {
int ans=1;int base=x;
while(y) {
if(y&1)ans=ans*base;
y/=2;base=base*base;
}
return ans;
}
int mod=1e9+7;
const int maxn=500001;
int leads[maxn];
int f[600010][15];
int graph[15][15];
int n,m,k;
signed main() {
read(n),read(m);
rep(i,1,n){
rep(j,1,n){
graph[i][j]=0x3f3f3f3f;
if(i==j)
graph[i][j]=0;
}
}
rep(i,1,m){
int x,y,z;
read(x),read(y),read(z);
graph[x][y]=min(graph[x][y],z);
graph[y][x]=graph[x][y];
}
memset(f,0x3f,sizeof(f));
// cout<<f[1][1]<<" ";
rep(i,0,n-1){
f[1<<i][1]=0;
}
if(n==1){
cout<<0;
return 0;
}
for( int i=1;i<=(1<<n)-1;++i) {
for( int j=0;j<n;++j) if(((1<<j)|i) == i) {
for( int k=0;k<n;++k) if(graph[j+1][k+1]!=0x3f3f3f3f) {
leads[i]|=(1<<k);
}
}
}
int ans=0x3f3f3f3f;
rep(j,2,n){
rep(i,1,(1<<n)-1){
for(int x=(i-1)&i;x;x=i&(x-1)){
if((leads[x]|i)!=leads[x])
continue;
int anss=0;
int bu=x^i;
rep(i1,0,n-1){
if((1<<i1)>bu)
break;
if((1<<i1)&bu){
int minn=0x3f3f3f3f;
rep(i2,0,n-1){
if((1<<i2)>x)
break;
if((1<<i2)&x){
minn=min(minn,graph[i1+1][i2+1]);
}
}
// cout<<i<<" "<<x<<" "<<minn<<" "<<j<<endl;
anss+=minn*(j-1);
}
}
f[i][j]=min(f[i][j],f[x][j-1]+anss);
}
if(i==(1<<n)-1)
ans=min(ans,f[i][j]);
}
}
cout<<ans;
return 0;
}
技巧3
上面的代码还用到了一个不能称之为技巧的技巧,就是变量bu的定义。其本质就是原集合和被枚举子集的补集,但这一优化方式同样值得注意。总的来说状压dp的精髓还是在于用尽量多的位运算和剪枝方式来加速计算,毕竟究其本质其更像是记忆化搜索而非真正意义上的优化dp方法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端