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

转移dp简单数学数论

1.转移dp问题

昨天的练习赛上有一个很好玩的起终点问题,第一时间给出bfs的写法。

但是写到后面发现不行,还得是的dp转移的写法才能完美的解决这道题目。

每个格子可以经过可以不经过,因此它的状态空间是2^(n*m),但是n,m的数据范围是500,显然是不可取的。bfs适用于计数或者最短距离,而不是最大和或最优路径问题。

故:对于最大和的问题dp是最合适的选择。

题目意思:

给定起点终点,每个点只能经过一次,找到最大的路径和,并且只能向下向右走动。

思路:

既然是dp那么一点有初始化,很容易想到第一列一定是固定的,因为该列只能像下走动(从起始点开始)。

那么之后我们就对每一列赋值(从第一列开始,每一列的状态都是从前面一列转移过来的)。

对于某一列的赋值,我们可以从头开始往下走,也可以是从尾开始走到第一行在进行继续走,那么这里就分成了两种情况。

我们先任意求出一种情况,然后在慢慢的用前缀和进行维护(因为是一条线下的,前缀和维护方便)。(毕)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int nima=8e18;
int a[504][504];
void solve(){int n,m;cin>>n>>m;int s,t;cin>>s>>t;s--,t--;for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>a[i][j];}}vector<vector<int>> dp(n,vector<int>(m,-nima));//这里的dp[i][j]的意思是从[s][0]开始到[i][j]的最大贡献// 初始化第一列的值int sum=a[s][0];for(int i=s;;){//一共要遍历s次dp[i][0]=sum;  // 初始化环形路径的第一个点i=(i+1)%n;     // 环形移动sum+=a[i][0];   // 累加路径上的值if(i==s) break; // 回到起点时结束}// 动态规划处理每一列for(int i=1;i<m;i++){int cnt=-nima;int sum=0;vector<int> pre(n);  // 前缀和数组// 正向遍历,计算从上方转移的最大值for(int j=0;j<n;j++){cnt=max(cnt,dp[j][i-1]-sum);  // 维护最大值,从左边过来的sum+=a[j][i];                   // 累加当前列的值dp[j][i]=cnt+sum;              pre[j]=sum;                     // 记录前缀和,这个sum是列环形状态下的前缀和}cnt=-nima;// 反向遍历,处理环形路径的情况for(int j=n-1;j>=0;j--){if(j!=n-1) dp[j][i]=max(dp[j][i],cnt+pre[j]);// 计算从下方转移的最大值(考虑环形路径)if(j!=0) cnt=max(cnt,dp[j][i-1]+pre[n-1]-pre[j-1]);}}cout<<dp[t][m-1]<<endl;
}signed main(){int ac=1;while(ac--)  solve();return 0;
}

2.简单数学

这次的团队赛有个简单数学问题,挺有意思的。

题目意思:

给出一个数组,找出最大贡献(每个贡献是相邻两个数字之差的绝对值)。

思路:

我们可以根据题目给的样例找到....

1 2 3 4 5 6 的最大贡献是9,即(3,4)(2,5) (1,6)状态下贡献是最大的。

我们进行改变之后发现....

3 2 1 4 5 6的最大贡献也是9,即(1,4)(2,5)(3,6)状态下贡献是最大的。

之后在进行任意举列子之后我们发现....

一组数据进行排序后每次最大贡献取法是首位找(毕)

小tips:数学问题,大胆猜,先排序,然后...(看看能不能瞎猫碰到死耗子)

#include<bits/stdc++.h>
using namespace std;
#define int long longinline void solve(){int n; cin >> n;vector<int> a(2 * n);for(int i = 0; i < 2 * n; i++) cin >> a[i];sort(a.begin(), a.end());//排序int answer = 0;for(int i = 0; i < n; i++) {answer+= abs(a[i] - a[2 * n - 1 - i]);//首位之差,参考1 2 3 4 5 6这个样例}cout << answer << endl;
}signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t; cin >> t;while(t--) solve();return 0;
}

3.数论

这次的数论有点点绕...

 题目意思:

给定一个数是好数m,只有m形如k!或者为偶数的条件下才成立。

给定一个数a,找到最少的分类情况k,使得k个好数之后是a。

思路:

观察题目的数据范围我们看到,n<=10^12,而且有t组数据,最好做一个状态压缩。

我们先对阶乘进行赋初值,15!>10^12。

每次减去1到15的阶乘,最后加上二进制中1的个数就是答案,每次枚举维护一个最小值即可。(毕)

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define pii pair<int, int> 
vector<int> v;inline void solve() {int sum = 1,i=1;  // 初始化阶乘结果为 1while(sum<=1e12){sum*=i++;v.push_back(sum);}//sort(v.begin(), v.end());  // 对向量 v 进行排序// 去除重复的阶乘结果//v.erase(unique(v.begin(), v.end()), v.end());int m = v.size();  int n;cin >> n; int ans = 1e9 + 7;// 遍历所有可能的子集(通过位掩码的方式)for (int i = 0; i <= (1 << m) - 1; i++) {int res = n;  // 初始化 res 为 n// 遍历每一位,检查是否在子集中for (int j = 0; j < m; j++) {if ((1 << j) & i)  // 如果第 j 位在子集 i 中res -= v[j];  // 从 res 中减去对应的阶乘值}if (res < 0) continue;  // 如果 res 为负数,跳过当前子集// 计算当前子集的位数和剩余数的位数之和,并更新最小值ans = min(ans, (int)__builtin_popcountll(res) + __builtin_popcountll(i));}cout << ans << endl;
}
signed main() {int nc;cin >> nc;while (nc--) solve();  
}

相关文章:

  • SAR ADC 的DAC 参考的选择逻辑
  • 精益数据分析(82/126):先行指标驱动的增长黑客策略——从相关性到因果性的跨越
  • ollama接入图像识别大模型
  • PINN高阶技术综合应用:复杂问题求解与神经算子进阶
  • C/C++STL---<chrono>
  • redis功能清单
  • 【Unity】使用InputSystem实现UI控件与键盘输入绑定以及如何快速制作虚拟摇杆
  • Pycharm和Flask的学习心得(7)
  • 236.二叉树的最近公共祖先
  • python web开发-Flask模板引擎Jinja2完全指南
  • 进一步学习线程相关知识
  • vue3中使用computed
  • Python 爬虫之requests 模块的应用
  • Vue组件通信的 `$attrs`与`$listeners`的优先级
  • 高效大型语言模型推理优化综述
  • Reason-ModernColBERT论文速览:Sentence- bert-基于孪生bert网络的句子嵌入
  • 基于SpringBoot+Vue的家政服务系统源码适配H5小程序APP
  • 人脸识别流程与算法对比报告
  • ES 面试题系列「三」
  • (已开源-CVPR2024) RadarDistill---NuScenes数据集Radar检测第一名
  • wap网站开发视频教程/河南网站建设定制
  • 长沙房产网最新楼盘/seo外链购买
  • 记事本做网站怎么改字体颜色/百度seo 优化
  • 做网站的注意事项/客户管理软件
  • 沙朗镇做网站公司/网站源码下载
  • html怎么学/合肥网站优化技术