给定一个 的数字矩阵和常数 ,初始位于 点,只能通过向下或者向右走来到达 点。
存在某种操作,可以选择任意一行 ,将其所有列元素逆时针旋转 个单位,这个操作可以对任意行进行任意次(下面称这个操作为“旋转”)。
设最后总操作次数为 ,经过的所有元素和为 ,最后的代价就是 ,求出这个代价可能的最小值。
注意:所有“旋转”操作需要在出发之前确定,或者等效来讲,不可以在某一行上移动时,对该行进行“旋转”操作。
这个问题很明显可以 DP,并且有两个显然的观察:
- 每一行的旋转与否、旋转的次数是彼此之间独立的。
- 任意一行的旋转次数不会超过 次,否则就是无用功。
那么就可以很自然的得到 DP 的定义: 为 到达 点,当前行旋转了 次的最小代价。
那么对于某个点 ,其只会从 和 转移。
从 转移的时候,完全可以不用关心 行旋转了多少次,正如上文 “1” 所说,行与行之间的旋转操作是独立的,我只需要知道到达 这个点的最小代价即可,这一过程可以通过在 DP 过程中随手记录下最小值实现。
从 转移的时候, 仅可从 转移,这一点在 “注意” 中已经指出。
那么这样一来整个流程就明了了,唯一需要再额外注意的地方在于可能会有数据溢出的情况。
| #include<bits/stdc++.h> |
| using namespace std; |
| int T,n,m,k; |
| const int N=2e5+10; |
| typedef long long ll; |
| const ll INF=1e15+1; |
| ll a[300][300]; |
| inline ll get(int i,int j,int add) |
| { |
| int tmp=(j+add)%m; |
| return tmp==0?a[i][m]:a[i][tmp]; |
| } |
| inline void solve() |
| { |
| cin>>n>>m>>k; |
| for(int i=1;i<=n;++i) |
| for(int j=1;j<=m;++j) |
| cin>>a[i][j]; |
| |
| vector<vector<vector<ll>>> dp(n+1,vector<vector<ll>>(m+1,vector<ll>(m+1,INF))); |
| vector<vector<ll>>mdp(n+1,vector<ll>(m+1,INF)); |
| mdp[0][1]=mdp[1][0]=0; |
| for(int i=1;i<=n;++i) |
| { |
| for(int j=1;j<=m;++j) |
| { |
| for(int x=0;x<=m-1;++x) |
| { |
| dp[i][j][x]=min(dp[i][j][x],mdp[i-1][j]+get(i,j,x)+1ll*x*k); |
| dp[i][j][x]=min(dp[i][j][x],dp[i][j-1][x]+get(i,j,x)); |
| mdp[i][j]=min(mdp[i][j],dp[i][j][x]); |
| } |
| } |
| } |
| cout<<mdp[n][m]<<'\n'; |
| } |
| int main() |
| { |
| ios::sync_with_stdio(0); |
| cin.tie(0);cout.tie(0); |
| cin>>T; |
| while(T--) |
| { |
| solve(); |
| } |
| } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!