ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

状压dp知识点

2021-03-17 23:57:29  阅读:136  来源: 互联网

标签:知识点 int 状压 cin 二进制 state dp


状压dp知识点

1、二进制基本技巧:

1:判断二进制上某一位是不是1:
    if( x>>i&1 == 1 )
    右移i位和1相与
        
2:将二进制第i位改为1:
    x = x | (1<<i);

3:将二进制第i位反转:
    x = x ^ (1<<i);

4:把二进制从右数第一个“1”舍弃:
    x = x & (x-1);

5:在二进制下,判断a与b,若b的位数中是1时,不允许a的位数中有0
    a & b != b

6:枚举x的子集:即枚举的数j的位数中,x是0的,j也一定要是0
    for(int j = x; j > 0; j = (j-1)&x )
        

2:经典例题1:最短Hamilton路径

题意:从 0 走到 n-1号点,途中以任意顺序经过其它所有点,求最短的路径。(给出边权)

数据范围: 1<=n<=20

暴力: O ( n ! ) = = 1 0 18 O( n! ) == 10^{18} O(n!)==1018

状压dp: O ( 2 n ∗ n 2 ) = = 1 0 8 O( 2^n *n^2 ) == 10^{8} O(2n∗n2)==108

AC 代码:

#include <bits/stdc++.h>
using namespace std;

int n;
int dp[1<<20][21];
int weight[21][21];

int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cin>>weight[i][j];
		}
	}
	
	memset(dp, 0x3f, sizeof(dp));
	
	dp[0001][0] = 0; //集合中只包含第0个点,当前驻足在 0 点
	
	// 遍历 00000001 ~ 11111111 点的集合 
	for(int state = 1; state < (1<<n) ; state ++){
		if(state & 1 == 1){      //集合必须包含 起点 
			for(int j=0;j<n;j++){
				if(state>>j&1 == 1){ //如果state二进制的j号位上为 1 
					for(int k=0;k<n;k++){
						int p = state ^ (1 << j);  //反转state第j位上的数  
						dp[state][j] = min(dp[state][j] , dp[p][k] + weight[k][j]);
					} 
				}
			}
		}
	} 
	
	// 二进制下:dp[111111111][n-1] :所有点都在集合中,并且当前在 n-1位置上  
	cout<<dp[(1<<n)-1][n-1]<<endl;
}

3、经典例题2:Close Group

题意:给定n个点m条边的无向图,
要求删掉若干边,使得图为若干个连通块,满足每个连通块都是完全图,
问最少的连通块数量是多少。

数据范围: 1 < = n < = 18 1<=n<=18 1<=n<=18

状压dp : d p [ m a x n ] : m a x n = 1 < < 18 dp[maxn]: maxn = 1<<18 dp[maxn]:maxn=1<<18 (2的18次方)

dp[i] : 当点集为i时,(如i=5,二进制位101,点集为 0,2号点),满足题面的最小连通块数目

转移方程 : d p [ s t a t e ] = m i n ( d p [ s t a t e ] , d p [ s t a t e − j ] + d p [ j ] ) ; dp[state] = min( dp[state] , dp[state - j] + dp[ j ] ) ; dp[state]=min(dp[state],dp[state−j]+dp[j]);

AC代码:

#include <bits/stdc++.h>
using namespace std;

int dp[1<<18];
int n,m;
int edge[20];

void init(){
	for(int i=0;i<n;i++)
		edge[i] = edge[i] | (1<<i);
}

int main(){
	cin>>n>>m;
	init();
	while(m--){
		int a,b,a1,b1;
		cin>>a>>b;
		a1=a-1 ,b1=b-1;        //二进制从 0 开始 ,点的序列号改为:0~n-1 
		edge[a1] = edge[a1] | (1<<b1);  //将 b-1位改成 1 
		edge[b1] = edge[b1] | (1<<a1);
	}
	
	memset(dp,0x3f,sizeof(dp));
	
	dp[0] = 0;
	
	for(int state = 1; state < (1<<n) ; state++){
		//判定集合state是否是完全图 
		int complete = 1; 
		for(int j=0;j<n;j++){
			//如果当前集合包含 j号点,但是 j点不与其它点连接,即不构成完全图 
			if( state>>j&1 && (edge[j]&state) != state){
				complete = 0;
			}
		}
		
		//完全图,则强连通分量为 1 
		if(complete) dp[state] = 1;
		
		//枚举当前集合的所有子集 
		//state二进制位上为 0的,j该位必须为 0
		for(int j=state; j>0; j=(j-1)&state){
			dp[state] = min(dp[state] , dp[state-j]+dp[j]);
		}
	}
	
	cout<<dp[(1<<n)-1]<<endl;
}

标签:知识点,int,状压,cin,二进制,state,dp
来源: https://blog.csdn.net/m0_50796573/article/details/114956561

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有