当前位置: 首页 > news >正文

状压DP-Hamilton问题

前置

此处仅为简写,详见OI Wiki

位运算

  1. 按位与 (&):1 & 1 = 1,1 & 0 = 0,0 & 0 = 0
  2. 按位或 (|):1 | 1 = 1,1 | 0 = 1,0 | 0 = 0
  3. 位取反 (~):~ 1 = 0,~ 0 = 1
  4. 按位异或 (^):1 ^ 1 = 0,0 ^ 0 = 0,1 ^ 0 = 1,0 ^ 1 = 1
  5. 左移 (<<):1<<1 = 10,1<<2 = 100
  6. 右移 (>>):100>>1 = 10,100>>2 = 1

特殊运算

给定一个十进制数 \(x\)

  • 判断 \(x\) 的第 \(i\) 位是否为 \(1\)x&(1<<i)

  • \(x\) 的第 \(i\) 位改为 1:x|(1<<i)

  • \(x\) 的第 \(i\) 为改为 0:x&(~(1<<i))

  • \(x\) 的第 \(i\) 为取反:x^(1<<i)

状压DP-Hamilton通路问题

例题1

一眼看,可以发现暴力做法就是全排列问题,枚举一遍取最优解

但是16!(不是感叹号是阶乘) 的运算次数显然不够我们造,所以我们要考虑优化

我们可以发现,当我们到达一个点时,我们实际上并不关注前面经过了哪些点,只要知道最后一个点的位置增加距离即可

所以考虑DP

  • 定义状态:定义\(dp_{status,i}\)表示在\(status\)(二进制)状态下,以\(i\)结尾的答案最小值(\(status\)\(1\)表示已经走过,\(0\)表示没走过),同时,因为有二进制,我们下标从0开始

  • 确定答案:\(dp_{(1<<n)-1,n-1}\),前一个表示了一个长度为\(n\)的全为\(1\)的二进制串,同时下标是\(0\)\(n-1\),所以结尾是\(n-1\)

  • 确定状态转移方程:\(dp_{status,i}=\min(dp_{status,i},dp_{status^(1<<i),j} +dis_{j,i})\)(在代码中解释)

  • 边界及初始值:\(dp_{i,j}=\inf\)\(dp_{1,0}=0\)

代码(附解释):

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e2+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int dp[(1<<17)-1][20];
int n;
int val[maxn][maxn];
signed main(){cin>>n; for(int i=0;i<n;i++){for(int j=0;j<n;j++){cin>>val[i][j];//输入距离}}memset(dp,0x3f3f3f3f,sizeof(dp));//边界dp[1][0]=0;//初始化for(int status=2;status<(1<<n);status++){//枚举所有状态for(int i=0;i<n;i++){//枚举状态中的每一位if((status>>i)&1){//判断i位是否为1for(int j=0;j<n;j++){//枚举其他位int tmp=status^(1<<i);//上一个状态if(i!=j&&(tmp>>j)&1){//判断上一个状态中j位是否为1dp[status][i]=min(dp[status][i],dp[tmp][j]+val[j][i]);//状态转移}}}}}cout<<dp[(1<<n)-1][n-1];//答案return 0;
}

习题1

与上一题类似,只是终点与起点一样,我们枚举起点外的所有点,选一个答案+回来路径最小的点,输出答案即可:

代码:

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e3+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int dp[(1<<21)-1][25];
int n;
int val[maxn][maxn];
signed main(){cin>>n; for(int i=0;i<n;i++){for(int j=0;j<n;j++){cin>>val[i][j];}}memset(dp,0x3f3f3f3f,sizeof(dp));dp[1][0]=0;for(int status=2;status<(1<<n);status++){for(int i=0;i<n;i++){if((status>>i)&1){for(int j=0;j<n;j++){int tmp=status^(1<<i);if(i!=j&&(tmp>>j)&1){dp[status][i]=min(dp[status][i],dp[tmp][j]+val[j][i]);}}}}}//DP过程int maxi=1e9;for(int i=1;i<n;i++){//枚举除起点外每个点if(dp[(1<<n)-1][i]+val[i][0]<maxi){//打擂台求最小值(这里变量写成最大值了)maxi=dp[(1<<n)-1][i]+val[i][0];}}cout<<maxi;return 0;
}
http://www.sczhlp.com/news/13381/

相关文章:

  • 用 Kotlin 和 Tesseract OCR 实现验证码识别
  • 在K8S中,网络模型有哪些?
  • 把数学对象画出来:Manim Mobject类库速查手册
  • (倍增优化dp)[NOIP2012 提高组] 开车旅行
  • 在K8S中,CNI模型有哪些?
  • 基于 Vue2+Quill 的富文本编辑器全方案:能力实现与样式优化
  • 5.5 with管理上下文
  • 在K8S中,网络策略有哪些?
  • 图片预览轮播组件-配置详解
  • 个位数统计
  • 在K8S中,网络策略原理是什么?
  • 中国计算机专家
  • 混合红队与蓝队训练实验室搭建指南
  • Java 中常用注解
  • 2025河南萌新赛第四场vp记录
  • 在K8S中,Calico 网络组件实现原理?
  • 在K8S中,flannel的作用?
  • 在K8S中,共享存储的作用?
  • 第三十九天(8.14) 方法重写(重载)
  • [Cursor] Notepads
  • [Cursor] 其它细节
  • Github Notes - 一个为GitHub仓库添加私人备注的浏览器扩展
  • [Tools] AI编码工具综合指南
  • MD5加密基本语法
  • 5.4 文件的三种打开方式
  • 代码的封装
  • CSP-S模拟12
  • 第三十八天(8.13) 继承
  • P2371 [国家集训队] 墨墨的等式
  • 8月16号