Order Capital Round 1 (Codeforces Round 1038, Div. 1 + Div. 2) ABCD题解

A. Greedy Grid

题意:

在一个网格中,一条路径被称为“贪心路径”,如果它从左上角的单元格出发,每一步只能向右或向下移动,并且每次总是移动到相邻的值更大的单元格(如果相等则任选其一)。
一条路径的价值是它经过的所有单元格的值之和,包括起点和终点。
是否存在一个 \(n×m\) 的非负整数网格,使得没有任何一条贪心路径能够取得所有向下/向右路径中的最大价值?

思路:

如果 $ n = 1 $ 或 $ m = 1 $,那么只有一条向下/向右的路径,因此它是显然贪婪且最大的。

如果 $ n = m = 2 $,任何路径在第一步之后都必须在两个邻居之间做出选择。显然,贪婪地移动总是会得到一条最大路径。

在所有其他情况下,这样的网格确实存在。假设 $ n \leq m $,从一个填充了1的 $ n \times m $ 网格开始,并修改左上角的 $ 2 \times 3 $ 子网格如下:

\[\begin{bmatrix} 1 & 2 & 3 \\ 3 & 1 & 1 \end{bmatrix}. \]

很容易验证,在这个网格中,没有贪婪路径能够达到可能的最大值。

代码

void solve() {
	 int n, m;
	 cin >> n >> m;
	 if (n == 1 || m == 1) {
	 	 cout << "NO" << "\n";
	 }else if (n == 2 &&  m == 2) {
	 	 cout << "NO" << "\n";
	 }else{
	 	 cout << "YES" << "\n";
	 }
}

B. Pile Shuffling

题意:

给定 n 个二进制堆,其中第 i 个堆顶部有 \(a_i\)个 0,底部有 \(b_i\)个 1。
每次操作中,你可以取出任意堆的顶部元素,并将其移动到任意堆的任意位置(包括原堆)。
计算最少需要多少次操作,才能使第 i 个堆形成顶部 \(c_i\)​ 个 0 和底部 \(d_i\)个 1 的目标状态。

思路:

先考虑 1,如果一个堆的 1 要减少,那么需要先把上面的 0 取走才能取出 1
再考虑 0,如果一个堆的 0 要减少,那么可以直接从顶部取走 0 然后插入到该插入的地方
因为“保证存在一系列操作可以将这些堆转换为目标状态”。所以只考虑减少的情况即可

代码

void solve() {
	 int n;
	 cin >> n;
	 ll zero = 0;
	 ll one = 0;
	 ll cnt = 0;
	 for (int i = 1; i <= n; ++i) {
	   ll a, b, c, d;
	   cin >> a >> b >> c >> d;
	   if (b > d) {
	   	 cnt += a + b - d;
	   }else if (a > c) {
	   	 cnt += a - c;
	   }	 
	 }
	 cout << cnt << "\n";
}

C. Manhattan Pairs

题意:

给你平面上的 n 个点(n 是偶数),你可以把这些点两两分成一组,共 \(\frac{n}{2}\)组。要求每一个组点对之间的曼哈顿距离之和最大,输出任意一种可能的方案。

思路:

将左上的点和右下的点配对,左下的点和右上的点进行配对

代码

#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;

const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
const int N = 2e5 + 10;

//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;

struct node {
	 int x, y, id;
};

bool cmp (node a, node b) {
	 return a.x < b.x;
}
bool cmp2 (node a, node b) {
	 return a.y < b.y;
}
node a[N];
void solve() {
	 int n;
	 cin >> n;
	 vector<node> b (n / 2 + 1);
	 vector<node> c (n / 2 + 1);
	 for (int i = 1; i <= n; ++i) {
	 	 cin >> a[i].x >> a[i].y;
	 	 a[i].id = i;
	 }
	 sort (a + 1, a + 1 + n, cmp);
	 for (int i = 1; i <= n / 2; ++i) {
	 	 b[i] = {a[i].x, a[i].y, a[i].id};
	 }
	 int q = n / 2;
	 for (int i = q + 1; i <= n; ++i) {
	 	 c[i - q] = {a[i].x, a[i].y, a[i].id};
	 }
	 sort (b.begin () + 1, b.end (), cmp2);
	 sort (c.begin () + 1, c.end (), cmp2);
	 for (int i = 1; i <= n / 2; ++i) {
	 	 cout << b[i].id << " " << c[n / 2 - i + 1].id << "\n";
	 }
}
int main() {
	 ios::sync_with_stdio (false);
	 cin.tie(NULL);
	 cout.tie(NULL);
	 t = 1;
	 cin >> t;
	 while (t --) {
	 	 solve();
	 }
	 return 0;
}

D. Traffic Lights

题意:

我们需要计算从顶点1到顶点n的最小总时间,以及在最小总时间的前提下的最小等待时间。
在顶点u,当时间为t时,我们有两种操作:
1、等待1秒:时间变为t+1,等待时间增加1,令牌位置不变。
2、通过第(t mod deg(u) + 1)条边移动

我们需要从顶点1到顶点n的最短总时间,以及在总时间最小的前提下最少的等待时间

思路:

①考虑bfs:

记录当前节点,当前时间,等待时间三个参数,在每一层更新两种状态。
MLE on test case 5
主要原因是BFS未对状态进行有效剪枝,导致状态爆炸

代码

#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;

const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
const int N = 5e3 + 10;

//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int tt;
vector<int> w[N];
int n, m;
bool flag = false;

struct node {
	 ll u, t, tw;
};

void solve() {
	 flag = false;
	 cin >> n >> m;
	 for (int i = 1; i <= n; ++i) w[i].clear ();
	 for (int i = 1; i <= m; ++i) {
	 	 int x, y;
	 	 cin >> x >> y;
	 	 w[x].push_back (y);
	 	 w[y].push_back (x);
	 }
	 queue<node> que;
	 ll rest = iinf, resw = iinf;
	 que.push ({1, 0, 0});
	 map<pair<int, int>,  int> mp;
	 while (!que.empty ()) {
	   auto it = que.front ();
	   que.pop ();
	   if (it.u == n) {
	   	 if (it.t < rest) {
	   	 	 rest = it.t;
	   	 	 resw = it.tw;
	   	 }else if (it.t == rest && it.tw < resw) {
	   	 	 rest = it.t;
	   	 	 resw = it.tw;
	   	 }
	   }else{
	   	 if (it.t > rest) continue;
	   	 int nn = w[it.u].size ();
	   	 mp[{it.u, it.t % nn}] = 1;
	   	 que.push ({w[it.u][it.t % nn], it.t + 1, it.tw});
         for (int i = 1; i <= nn; ++i) {
	 	 	 que.push ({it.u, it.t + i, it.tw + i});
	     }	
	   }
	 }
	 cout << rest << " " << resw << "\n";
}
int main() {
	 ios::sync_with_stdio (false);
	 cin.tie(NULL);
	 cout.tie(NULL);
	 tt = 1;
	 cin >> tt;
	 while (tt --) {
	 	 solve();
	 }
	 return 0;
}

②考虑dp:

用dp[i]表示从起点1到达节点i所需的最小额外等待时间
​​时间迭代​​:外层循环j表示当前时间步(从0开始递增)
​​操作类型​​:
​​①等待操作​​:在当前节点多停留一个时间步(增加等待时间)
​②​移动操作​​:按当前时间j选择对应出边移动(不增加等待时间)
​​终止条件​​:当终点n的dp[n]首次不为INF时输出当前时间步j和等待时间dp[n]

#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;

const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
const int N = 5e3 + 10;

//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;
vector<int> w[N];

void solve() {
	 int n, m;
	 cin >> n >> m;
	 vector<ll> dp (n + 1, 0);
	 for (int i = 1; i <= n; ++i) w[i].clear (), dp[i] = iinf;
	 for (int i = 1; i <= m; ++i) {
	 	 int x, y;
	 	 cin >> x >> y;
	 	 w[x].push_back (y);
	 	 w[y].push_back (x);
	 }
	 dp[1] = 0;
	 for (int i = 0; ; ++i) {
	 	 if (dp[n] != iinf) {
	 	 	 cout << i << " " << dp[n] << "\n";
	 	 	 return ;
	 	 }else{
	 	 	 vector<ll> new_dp = dp;
	 	 	 for (int j = 1; j <= n; ++j) {
	 	 	 	 if (new_dp[j] != iinf) {
	 	 	 	 	 new_dp[j] = dp[j] + 1;
	 	 	 	 }
	 	 	 }
	 	 	 for (int j = 1; j <= n; ++j) {
	 	 	 	 if (new_dp[j] == iinf) {
	 	 	 	 	 continue;
	 	 	 	 }else{
	 	 	 	 	 int nn = w[j].size ();
	 	 	 	 	 if (nn == 0) continue;
	 	 	 	 	 int ne = w[j][i % nn];
	 	 	 	 	 new_dp[ne] = min (new_dp[ne], dp[j]);
	 	 	 	 }
	 	 	 }
	 	 	 dp = new_dp;
	 	 }
	 }
}
int main() {
	 ios::sync_with_stdio (false);
	 cin.tie(NULL);
	 cout.tie(NULL);
	 t = 1;
	 cin >> t;
	 while (t --) {
	 	 solve();
	 }
	 return 0;
}
posted @ 2025-07-30 21:02  Li_Yujia  阅读(9)  评论(0)    收藏  举报