首页 > 编程语言 >【算法每日一练]-动态规划(保姆级教程 篇17 状态压缩)

【算法每日一练]-动态规划(保姆级教程 篇17 状态压缩)

时间:2024-04-08 11:58:05浏览次数:27  
标签:状态 教程 17 int s2 cnt 算法 num dp

 目录

今日知识点:

把状态压缩成j,dp每行i的布置状态,从i-1和i-2行进行不断转移

把状态压缩成j,dp每行i的布置状态,从i-1行进行状态匹配,然后枚举国王数转移

 POJ1185:炮兵阵地

思路:

题目:互不侵犯

思路:


        

        

 POJ1185:炮兵阵地

在N*M(N<100,M<10)的地图上布置炮兵,H格子为山地不能布置,P格子为平原可以布置。炮兵的攻击范围是沿横向左右各两格,沿纵向上下个两格子
炮兵之间不能误伤。问在整个地图中最多能拜访多少个炮兵?
5 4
PHPP
PPHH
PPPP
PHPP
PHHP

        

思路:

还是那么句话,每个格子都有2种状态,如果搜索就要把所有结果都跑出来,所以只能使用状压dp 。      

而且方程的转移和上一行的状态有关,但是状态又太多了,故要状态压缩。

首先要对行进行状态压缩(对列的话太大了,枚举2^100还不如不压缩呢),我们每次确定行的状态都需要考虑:
1.横向方案    2.横向方案是否和地图匹配     3.是否和i-1行i-2行冲突
设置dp[i][j][k]表示第i行为第j状态,第i-1行为第k状态时 对应的前i行放置的最大炮兵数。
转移方程:dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]);(num是对应状态的炮兵个数)

(j是第i行的方案,k是第i-1行的方案,t是i-2行的方案)
存每行的可能状态:左右相邻1个间隔和2个间隔都不能炮兵(就是可能的横向方案)
存图:(1,1)开始存。0表示平原,1表示山地,那么在放置的时候不能出现同1(在山地放炮兵),所以x&y=0是合法的(保证合法的是0就行了)
是否冲突:第i行和第i-1行,第i-2行 不能出现有一列同1(两行都放炮兵),所以x&y=0是合法的

        
【注意】:外面每行i循环一次,其次里面是第i行的每个状态j循环一次(找到合适的j),然后是第i-1行的每个状态k循环一次(供第i行找到合适的k),
接着是第i-2行的每个状态t循环一次(供第i-1行找到合适的t)

#include <bits/stdc++.h>
using namespace std;
int n,m,top;
char mp[110][20];
int num[70];//num存放状态对应的炮兵个数
int ok[70],cur[70];//ok表示横向可能的方案,cur是我们存的地图行
int dp[110][70][70];

bool check(int x){
	if(x&(x<<1))return 0;//相邻1间隔是否合法
	if(x&(x<<2))return 0;//相邻2间隔是否合法
	return 1;
}

void init(){//统计所有的可能合法状态,最多60种
	top=0;
	for(int i=0;i<(1<<m);i++){//不能取等,不能取等!!!
		if(check(i))ok[top++]=i;
	}
}

int count(int x){//统计x二进制中1的个数
	int cnt=0;
	while(x){
		if(x&1)cnt++;
		x=x>>1;
	}
//	while(x){//这个更快
//		cnt++;
//		x&=(x-1);
//	}	
	return cnt;
}

int solve(){
	int ans=0;
	memset(dp,-1,sizeof(dp));
	for(int j=0;j<top;j++){//初始化第一行的状态
		num[j]=count(ok[j]);//记录每个正确状态的炮兵个数
		if(!(ok[j]&cur[1])){//和地图匹配
			dp[1][j][0]=num[j];//第一行状态为j,上一行状态为0(知道为啥从(1,1)开始初始化了把)
			ans=max(ans,dp[1][j][0]);
		}
	}

	for(int i=2;i<=n;i++){//处理每一行
		for(int j=0;j<top;j++){//遍历第i行的可能方案
			if(ok[j]&cur[i])continue;//是否和地图匹配
			for(int k=0;k<top;k++){//遍历第i-1行的可能方案
				if(ok[j]&ok[k])continue;//此行和上一行是否匹配(不用再判断和地图是否匹配,不匹配dp是-1,不影响的)
				for(int t=0;t<top;t++){//遍历上二行可能方案
					if(ok[j]&ok[t])continue;//此行和上二行是否匹配
					dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]);
				}
				if(i==n)ans=max(ans,dp[i][j][k]);//不要放在外面套3个for取max
			}
		}
	}
	return ans;
}
int main(){
	while(cin>>n>>m){
		init();
		for(int i=1;i<=n;i++){
			scanf("%s",mp[i]+1);//加1是为了从1下标开始存
		}
		for(int i=1;i<=n;i++){
			cur[i]=0;
			for(int j=1;j<=m;j++){
				if(mp[i][j]=='H')//同样的,不能放的地方存1
					cur[i]+=(1<<(m-j));
			}
		}
		cout<<solve();
	}
}

        

        

题目:互不侵犯

思路:

可以看到和炮兵阵地题非常像,跑不了是状压dp。

还是那句话,每个方格都有两种状态,搜索的话必然是要全部搜索一下,放弃吧(况且,不谈超时,你真的把本道题的dfs其实也够呛的)。

设置dp[i][j][k]表示第i行为j状态时已经放了k个国王对应的方案数。

转移方程:dp[i][j][k]=dp[i-1][i-1行所有的合法状态][k-对应状态的国王数];

然后就是对状态的处理:

行内状态:我们要保证相与出0合法,非0不合法。那么对s行,

有:(((s<<1)&s==0)&&((s>>1)&s)==0)算合法,

等价于这个写法:(((s<<1)|(s>>1))&s)==0。   千万别少写红色括号!!!
行间状态:((s2&s1==0)&&((s2>>1)&s1==0)&&((s2<<1)&s1)==0)才算合法,

等价于:(((s2|(s2>>1)|(s2<<1))&s1)==0。

循环方式:首先是每行,然后选择该行状态,然后是上行状态,判断两行状态是否匹配,如果匹配就枚举国王数,最后转移方程!

#include <bits/stdc++.h>
using namespace std;
int num,n,k;//ok存正确的行状态,cnt存状态对应的国王数
long long dp[10][100][2000],cnt[2000],ok[2000];
int main(){
	cin>>n>>k;
	for(int s=0;s<(1<<n);s++){//遍历出所有的正确状态i
		int tot=0,tmp=s;
		while(tmp){
			if(tmp&1)tot++;tmp>>=1;
		}
		cnt[s]=tot;//存每个状态的国王数
		if((((s<<1)|(s>>1))&s)==0) ok[++num]=s;
	}
	dp[0][0][0]=1;
	for(int i=1;i<=n;i++){//从第一行开始转移
		for(int ss1=1;ss1<=num;ss1++){
			int s1=ok[ss1];//选择第一行的状态
			for(int ss2=1;ss2<=num;ss2++){
				int s2=ok[ss2];//选择上一行的状态
				if(((s2|(s2<<1)|(s2>>1))&s1)==0){//如果这两行状态合法
					for(int j=0;j<=k;j++){//对国王数进行枚举转移
						if(j-cnt[s1]>=0)
							dp[i][j][s1]+=dp[i-1][j-cnt[s1]][s2];
					}
				}
			}
		}
	}
	long long ans=0;
	for(int i=1;i<=num;i++)
		ans+=dp[n][k][ok[i]];
	cout<<ans;
}

标签:状态,教程,17,int,s2,cnt,算法,num,dp
From: https://blog.csdn.net/m0_69478376/article/details/137502860

相关文章

  • 常见的排序算法——插入排序(二)
    本文记述了插入排序微小改进的基本思想和一份参考实现代码,并在说明了算法的性能后用实验进行了验证。◆思想内存中的数据交换是昂贵的操作,此改进实现了不需要交换的插入排序。将第一个元素之后的所有元素作为待排序范围,将前面的所有元素作为已排序范围。通过一一比较,逐个将已......
  • Midjourney api 国内对接使用教程
    项目背景众所周知,Midjourney并没有提供任何的Api服务,但是基于Midjourney目前的行业龙头位置,很多企业以及个人会有相关的需求。TTApi平台基于Midjourney现有功能整理出一套完整的可集成性高的服务,如果你有类似的需求,那么恭喜你找到了正确的使用方式。新用户注册免费送100配......
  • Manim教程之轻松制作数学动画
    【2024最新教程】Manim动画软件教程,像3blue1brown那样做动画【更新中】【2024最新教程】Manim动画软件教程,像3blue1brown那样做动画【更新中】_哔哩哔哩_bilibiliManim教程之轻松制作数学动画Manim教程之轻松制作数学动画_哔哩哔哩_bilibili我找到了3Blue1Brown做视......
  • 自适应鱼群算法改进随机森林的变压器故障诊断(IFSA-RF模型)(Matlab代码实现)
    ......
  • 自适应鱼群算法改进随机森林的变压器故障诊断(IFSA-RF模型)(Matlab代码实现)
    ......
  • 蓝桥杯-算法赛第9场强者:贝贝的2.0
    题意:n个节点的有根树,问孩子节点最少是多少,可以满足任意两条长度为k的链有公共节点。思路:一开始想的是以根为中间点,然后构造边。但是发现样例过不了,样例说的很清楚,根节点也作为一个叶子节点去构造,然后把叶子节点作为中间点(这样可以省去一个叶子节点的计数)。最后就是如何处理的问题......
  • 计算机毕业设计项目:springboot 智能答疑系统 96852(开题答辩+程序定制+全套文案 )上万套
    毕业论文(设计) 题   目springboot智能答疑系统学   院       XXXXX     专业班级   XXXXX学生姓名       XXXX    指导教师            XXXX          撰写日期:202 年 月 日目 录摘要......
  • 计算机毕业设计项目:新生儿疫苗接种管理系统 87023(开题答辩+程序定制+全套文案 )上万套
    PHP新生儿疫苗接种管理系统系   院XXXX学科门类XXX专   业 XXX班级XXX学   号XXX姓   名XXX指导教师XXX教师职称XXX摘 要新生儿计划免疫是根据危害儿童健康的一些传染病,利用安全有效的疫苗,按照规定的免疫程序进行预防接种,......
  • 在线CAD二次开发教程-实现圆转多边形功能的方法
    前言在线CADSDK的集成过程中,甲方客户可能有实现圆转多边形功能的需求,作为开发者如何利用WEBCADSDK展现此功能效果呢?本章节我们重点讲述一下。环境搭建1.搭建绘图环境,创建一个mxcad项目,具体操作请参考[mxcad|快速入门]。2.在项目中添加命令行,实现功能的动态交互功能,具体......
  • 敏感词检测-DFA算法笔记及实现
    引子敏感词检测,这个是很多文字类服务都要遇到的问题,最近项目上接触到,特此调研梳理下这部分的内容。比如当我们输入一些包含暴力或者色情的文本,系统会阻止信息提交。敏感词过滤就是检查用户输入的内容有没有敏感词。OK,让我们开始吧。一、算法原理简介一般敏感词检测之后......