做题小结 DP训练5

第一

这道题可以用dp写也可以暴力去写
着重介绍暴力写法

首先观察数据范围 我们发现n m 都很小 考虑暴力

那如何暴力呢 最原始的想法就是一个个匹配
我们先假设答案都是全选第一个的
如果不是 那么假设第一行选第二个 别的还是都选第一个 然后第一行枚举完 发现没有出答案 证明第一行所有数字都相同 因为全选第一个还是为0 第一行的只要出现不同的数字就可以结束了 但没有 证明不行

噢 接下来我写任何题解 我都会把题目的图片发出来 让自己以后好看点

所以就是朴素的写


	for(int i=1;i<=n;i++){
		for(int j=2;j<=m;j++){
			if(a[i][j]!=a[i][1]){
				cout<<"TAK"<<endl;
				for(int k=1;k<i;k++){
					cout<<"1 ";
				}
				cout<<j<<" "<<endl;
				for(int k=i+1;k<=n;k++){
					cout<<"1 ";
				}
				return ;
			}
		}
	}

解下来介绍dp写法 跟之前做过的题目类似 我们这里假设选到某一个行时二维此时存储答案 然后转移就是从这一行进行转移
不过要预处理第一行的 方便后续转移

	for(int i=2;i<=n;i++){
		for(int j=1;j<=m;j++)
		for(int k=0;k<=1023;k++){
			if(dp[i-1][k]){
			dp[i][k^a[i][j]]=1;
			prefix[i][k^a[i][j]]=j;//二维前驱写法值不算大的情况下
			}
		}
	}

然后这边还要介绍下二维的前驱数组的写法
首先pre他的值要等于某一列的编号 然后二维的存储值 就行了 第一维照常储存行 然后进行while循环输出

for(int j=1;j<=1023;j++){
		if(dp[n][j]){
			cout<<"TAK"<<endl;vector<int>ans;
			ans.push_back(prefix[n][j]);
			int w=j^a[n][prefix[n][j]];//从n行的第j个转移过来 得到n-1时的数
			int temp=n-1;
			while(temp){
	
				ans.push_back(prefix[temp][w]);
				w=w^a[temp][prefix[temp][w]];
				temp--;
			}
			reverse(ans.begin(),ans.end());
			for(auto i:ans){
				cout<<i<<" ";
			}
			cout<<endl;
			return ;
		}	
	}

下一个

这道题我一开始想写记忆化 但是写不了 于是写成了dfs直接t掉了

记忆化不了 这种题就是一个断点 思考起来不难
写for循环其实浪费的话 是浪费的 但是记忆化写不了 我记忆化不太好 写不怎么来

//我思考了dp 可是我觉得写法不是很明了 就不想写了 其实是不会写qaq怕写错
	//我写不来记忆化的 于是t了 
	//不是写不来吧 主要我觉得双层for的话浪费太多但是记忆化我发现写不了啊
	//双层for就是有些地方会浪费 记忆化不知道咋写 
	//记忆化不了吧 这玩意 
	//这很明显需要多次更改 不是没后效性的玩意

滑雪记忆化

牛客这一道滑雪就是记忆化 记忆化写起来一定要满足一个条件 访问了的东西一定不需要再次进行dfs 此时的答案一定是最优解 我这道题很明显不是 一个点可能从上面更新了 但是不是最优了 由右面更新过来反而是最好的 势必重新更新他 那么此时之前的状态全部重新更新 不满足记忆化的最优思想

	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cin >> a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i][j];
		}
	}
	for(int i=n;i>=1;i--){
		for(int j=m;j>=1;j--){
		dphou[i][j]=max(dphou[i+1][j],dphou[i][j+1])+a[i][j];	
		}
	}
	for(int i=n;i>=1;i--){
		for(int j=1;j<=m;j++){
		f[i][j]=max(f[i+1][j],f[i][j-1])+a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=m;j>=1;j--){
		//浪费就浪费了
		fhou[i][j]=max(fhou[i-1][j],fhou[i][j+1])+a[i][j];	
		}
	}

对于这个以后时间过得去 都这么写 因为这个浪费就浪费了 这个写起来蛮简单的
然后这道题就是一个简单的for循环就可以代替dfs了

然后对于这个相遇点 题目没说明白 后面公告说就是路径只有一个交点的意思

此时暴力枚举 但是注意了 对于i=1或者i=n的都不行 j也同理 为什么呢
对于这个相遇点 非常的需要探讨如何走过去

这边我就把别人图拿来了


假设左上的那个人和左下要在O相遇

则对于左上的人来说 他只能从 1 2进入O 如果选择2 那么他不可以说从1出来
也不能从3 为什么呢 如果从3出来 那么 2 3 都被占领了 那么4 是不可能从1出来 反之也是 所以说 2 只能4出来 就是直直的走 同理选1那么从3出来

于是我们就得到了 2 -4 搭配1 -3 1 -3 搭配4 -2 这样的组合 所以转移方程有两种可能选最小就行

	for (int i = 2; i <n ; i++) {
		for (int j = 2; j <m; j++) 
	int w = dp[i][j-1] + dphou[i][j+1];
			int ww = f[i+1][j] + fhou[i-1][j];
			int p=dp[i-1][j]+dphou[i+1][j];
			int pp=f[i][j-1]+fhou[i][j+1];
}

此时我们也解释下为什么 for循环从2 很显然只要有1 就一定路径重复不止一个点

posted @   想念不动声色  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示