11.18题解

climb

首先我们有一个状态时 \(O(nV)\) 的 DP,即考虑到第 i 个点,当前高度是 j 的最小代价。

这个状态太大了,我们考虑哪些状态时冗余的。

考虑我一个点经过调整,如果前一个点确定是 t,那么当前这个点可能且只可能是:t - d, t + d, a_i。

那么这个东西是一个 a_i + kd 的形式的,我们就可以将状态精简为 \(O(n^3)\) 的。

然后考虑到这个转移是一个滑动窗口的形式,则最终复杂度是 \(O(n^3)\)

代码
#include <bits/stdc++.h>
using namespace std;

namespace myb {
	
    #define int long long
	const int N = 305;
	
	int a[N];
	int f[2][N * N * 2];
	
	void solve() {
		int n, d;
		cin >> n >> d;
		for (int i = 1;i <= n;i++) cin >> a[i];
		vector<int> x;
		for (int i = 1;i <= n;i++) {
            // if (d == 0) {
            //     x.push_back(a[i]);
            //     continue;
            // }
			for (int j = -n;j <= n;j++) {
                if (a[i] + 1ll * j * d < 0) continue;
				x.push_back(a[i] + 1ll * j * d);
			}
		}
		sort(x.begin(), x.end());
		x.erase(unique(x.begin(), x.end()), x.end());
		int m = x.size();
		for (int i = 0;i <= 1;i++) {
			for (int j = 0;j < m;j++) {
				f[i][j] = 1e16;
			}
		}
        int ind = 0;
		for (int i = 0;i < m;i++) {
			if (x[i] == a[1]) f[ind][i] = 0;
		}
        ind = 1 - ind;
		for (int i = 2;i <= n;i++, ind = 1 - ind) {
//			cout << i << ":\n";
            // cout << i << " " << ind << "\n";
			deque<int> q;
			int pos = 0;
			for (int j = 0;j < m;j++) {
				while (pos < m && x[pos] <= x[j] + d) {
					while (q.size() && f[1 - ind][q.back()] >= f[1 - ind][pos]) q.pop_back();
					q.push_back(pos);
					pos++;
				}
				while (q.size() && x[q.front()] < x[j] - d) q.pop_front();
				if (q.size() == 0) break;
				f[ind][j] = f[1 - ind][q.front()] + abs(a[i] - x[j]);
                // cout << f[ind][j] << " ";
//				cout << x[j] << ": " << f[i][j] << "\n";
			}
            // cout << "\n";
//			cout << "?\n";
		}
        // cout << ind << "\n";
		for (int i = 0;i < m;i++) {
			if (x[i] == a[n]) {
				if (f[1 - ind][i] > 1e15) cout << "-1\n";
				else cout << f[1 - ind][i] << "\n";
				return ;
			}
		}
	}
	void main() {
		int T;
        T = 1;
		// cin >> T;
		while (T--) {
			solve();
		}
	}
}

signed main() {
    freopen("climb.in", "r", stdin);
    freopen("climb.out", "w", stdout);
	myb::main();
	return 0;
}

fun game

首先清掉是别人子串的字符串,这些一定无用。

然后我们需要注意到一个关键性质:完成以上操作后,一定不会有一个字串绕过最终答案的那个环一整圈。

因为如果绕过去了,就意味着所有串都是他的子串。

然后我们就是一个字符串拼接的顺序问题,这个直接状压 DP。

line game

可以发现,我们要选择的线不会交叉,i 和 \(p_i\) 是分别递增的。

那我可以令 \(f_i\) 是选择第 i 条边删去,前面的全部都删掉了的最小花费,那么这个就应该从最近的一个 j,满足 \(p_j < p_i\) 转移而来。

那么我们怎么找到这个 j 呢?

posted @ 2025-11-20 16:06  yanbinmu  阅读(3)  评论(0)    收藏  举报