Educational Codeforces Round 133 (Rated for Div. 2)部分题解

C. Robot in a Hallway

题意:

给定一个2*m的格子,起点是(1,1),要求遍历完其他格子,且每个格子只能走一次。每个格子(i, j)有设定进入时间a[i][j],表示在时刻a[i][j]之前,不能访问该格子。

问按要求,遍历完所有格子,且满足上述条件的情况下,最少需要多少时间。

题解:

因为既要遍历所有的格子又要不重复,所以路线是很有限的,除了从起点开始就一直走蛇形路线,即一直下右上右循环外,当不按照这种方式走的时候,为了满足条件,行径路线就会变成唯一,即只能走从该点走到最右端再从另一行的最右端返回的这种U型路线。

首先维护一个数组f[i][j],表示从i行j列开始向右流畅走U型路线的最小值,流畅走就意味着当你走到点的时候无需等待格子解锁,即贪心策略就是先在原地等待一段时间,此后保证走到哪个点,哪个点一定是解锁状态。

转移方程如下:

f[i][j] = max(f[i][j + 1] - 1, max(a[i ^ 1][j] - 2 * (n - j) - 1, a[i][j]));

具体代码如下:

#pragma GCC optimize(2)
#include <iostream>
#include <vector>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <bitset>
#include <unordered_map>
#include <set>
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define endl "\n"
#define pb push_back
#define all(x) x.begin(), x.end()

using namespace std;

const int N = 200010, mod = 1000000007, INF = 1e9;
typedef long long LL;
typedef pair<int, int> PII;

int n, T;
int x;
int f[2][N];
int a[2][N];

void solve()
{
    memset(f, 0, sizeof f);
    cin >> n;
    for (int i = 0; i < 2; i ++ )
        for (int j = 1; j <= n; j ++ )
            cin >> a[i][j];
    
    a[0][1] = -1;
    //无需特判第一个点是否需要等待,然后来判断初始化res是加 2n-1 or 2n
    
    f[0][n] = max(a[0][n], a[1][n] - 1);
    f[1][n] = max(a[1][n], a[0][n] - 1);
    for (int i = 0; i < 2; i ++ )
        for (int j = n - 1; j >= 1; j -- )
            f[i][j] = max(f[i][j + 1] - 1, max(a[i ^ 1][j] - 2 * (n - j) - 1, a[i][j]));
//    cout << f[0][1] << endl;
    LL res = f[0][1] + 2 * n, rec = a[1][1] + 1;
    for (int col = 2, row = 1; col <= n; col ++ , row ^= 1)
    {
        res = min(res, rec + max((LL)f[row][col] - rec, (LL)0) + 2 * (n - col + 1));
        rec = max(rec + 1, (LL)a[row][col] + 1);
        rec = max(rec + 1, (LL)a[row ^ 1][col] + 1);
    }
    res = min(res, rec);
    cout << res << endl;
}

int main()
{
    quick_cin();
    cin >> T;
//    T = 1;
    while (T -- )
    {
        solve();
    }
    return 0;
}

D. Chip Move

题意:

在一维坐标中,从 0 位置开始跳。

第 1 次移动可以走 k 的倍数距离,第 2 次移动可以走 k+1 的倍数距离,…,第 m 次跳可以走 k+m-1 的倍数距离。
问,跳到 1~n 位置的方案数分别为多少?

题解:

Part1:dp 状态设计与暴力 dp。

首先将步数和目前到达的位置设为状态,即f[i][j]表示走了 i 步之后到达 j 的方案数。

此时临界状态f[0][0]=1

遍历所有位置 i,遍历跳的次数 j,遍历上一次跳的倍数 b,所以转移方程为:

f[i][j] += f[i - b * (k + j - 1)][j - 1];

但是此时需要我们枚举步数,枚举目前所在的下标,还要枚举出发点,总的时间复杂度为 O\left ( n^{3} \right ),不能通过本题。

Part 2:缩小值域优化时空复杂度。

设 m 为 chip 可以走的步数,则有下式:

k+(k+1)+(k+2)+...+(k+m-1)=\frac{(k+k+m-1)\times m}{2}\leqslant n

当 k=1 时 m 可以取最大值。我们发现 m 的最大值不会超过 \sqrt{2n},也就是说,我们可以把步数的值域缩小到根号级别的,这样时间复杂度就会达到\mathop{O(n^2\sqrt{n})},还是无法通过本题。

Part 3:利用前缀和优化时间复杂度。

考虑把原先的状态转移方程拆开:

f_{i,j}=f_{i-(k+j-1),j-1}+f_{i-2\times (k+j-1),j-1}+...+f_{i-num\times (k+j-1),j-1}

引入:

f_{i-(k+j-1),j}=f_{i-2\times (k+j-1),j-1}+f_{i-3\times (k+j-1),j-1}+...+f_{i-num\times (k+j-1),j-1}

于是联立得到:

f[i][j] = f[i - (k + j - 1)][j] + f[i - (k + j - 1)][j - 1];

此时时间复杂度降到了\mathop{O(n\sqrt{n})},但此时空间复杂度是\mathop{O(n\sqrt{n})},依旧不行。

Part 4:利用滚动数组优化空间复杂度。

然而这一道题还没有做完。\mathop{O(n\sqrt n)}的空间复杂度是我们接受不了的,观察发现第 i 步的状态只和第 i-1 步有关,所以我们可以利用滚动数组滚掉一维,空间复杂度便能降到\mathop{O(n)},可以通过本题。

具体代码如下:

#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#include <set>
#include <algorithm>
#include <vector>
#include <queue>
#define int long long
#define quick_cin() cin.tie(0),ios::sync_with_stdio(false) 
#define endl "\n"
#define pb push_back
#define all(x) x.begin(), x.end()

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;

const int N = 200010, mod = 998244353, INF = 1e18;

int T;
LL n, m, k;
int f[N][2];
LL ans[N];

void solve()
{
	cin >> n >> k;
	
	f[0][0] = 1;
	
	for (int j = 1; j * j <= 2 * n; j ++ )
	{
		for (int i = 1; i <= n; i ++ )
			if (i >= k + j - 1) 
				f[i][j % 2] = (f[i - (k + j - 1)][j % 2] + f[i - (k + j - 1)][(j % 2) ^ 1]) % mod;

		for (int i = 0; i <= n; i ++ ) 
		{
			ans[i] = (ans[i] + f[i][j % 2]) % mod;
			f[i][(j % 2) ^ 1] = 0;
		}
	}
	
	for (int i = 1; i <= n; i ++ ) cout << ans[i] << " ";
}

signed main()
{
//	quick_cin();
//	cin >> T; 
	T = 1;
	while (T -- )
	{
		solve();
	}
	
	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值