持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情
D-阿强与网格
题目描述
阿强在一个N行M列的网格中。
阿强可以用两种方式移动:
向下、向左、向上或向右移动,每次移动的代价为X。换句话说,如果您位于网格的单元格(i,j),则可以转到任意单元格(i+1,j),(i,j−1) ,(i−1,j)或(i,j+1),代价为X。
沿对角线向左下、向右下、向左上、向右上移动成本为Y。换句话说,如果你在网格的单元格(i,j)上,你可以去任意一个单元格(i+1,j−1) ,(i+1,j+1),(i−1,j−1) 或(i−1,j+1),代价为Y。
请你找到从阿强从左上角(1,1)到右下角(N,M)的最小成本。
阿强不能移出网格。
输入描述:
第一行一个整数T(1≤T≤5∗105)表示数据组数。
接下来T行每行四个整数N,M,X,Y(1≤N,M,X,Y≤106)
输出描述:
对于每组数据一行表示答案。
示例1
输入
3
5 6 2 5
4 7 5 6
7 8 6 5
输出
18
33
36
问题解析
一开始下意识的想BFS,后来发现根本不用那么麻烦(而且也过不了)。
题目已经给定了我们每次操作所花成本,我们可以根据x和y的大小,与网格的形状直接计算得出结果,为了方便,我们认定n比m大:
- 如果网格是n *1的形状,此时显然只能用x的方式移动,成本为:**(n-1)x*;
- 因为y走一步等于x走两步,所以如果x的两倍小于等于y,我们直接用x走,成本为:**(n+m-2)x*
剩下的情况,我们显然是能用y走就用y走,问题是y改怎么走呢?
一开始想着先对角线移动到底部,然后剩下的用x平移过去:(红色路线)
后来发现,其实剩下的位置也是可以斜着走的:(蓝色路线)
只不过这么走有约束,即到了底边后,距离终点的距离如果是奇数,则会停在终点的前一格,而这时我们只能用x走了:(橙色路线)
那么对于其它的情况:我们先走m次y,如果距离终点的距离:n-m是奇数,我们走了(n-m-1)次y后,还要走一次x,成本为:m *y+(n-m-1) *y+x ;如果n-m是偶数,我们直接全走y即可,成本为:m * y+(n-m) * y.
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 4e5 + 50;
void solve()
{
int n, m, x, y,ans;
cin >> n >> m >> x >> y;
//为了方便,我们保证n比m大
if(n<m)swap(n,m);
if(m==1)ans=(n-1)*x;
else if(x*2<=y)ans=(n+m-2)*x;
else if(x<=y)ans=(m-1)*y+(n-m)*x;
else
{
if((n-m)%2==1)ans=(n-2)*y+x;
else ans=(n-1)*y;
}
cout<<ans<<endl;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}