做题小结 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 就一定路径重复不止一个点
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!