2020-2021 ACM-ICPC, Asia Seoul Regional Contest

2020-2021 ACM-ICPC, Asia Seoul Regional Contest

D. Electric Vehicle

  • \(f[t][pos][r]\) 表示施展 \(t\) 次后,在 \(pos\),剩 \(r\) 的油最小耗费。
  • key 状态:在充电站上,油空,油满。
  • 一个关键的观察是:存在最优方案,不会在路径上连续两个充电站,不到 key 状态。
    • 因为:可以在便宜的那多充点,调整方案。
  • 只用关键状态转移即可。
    1. f[t][pos][0] => f[t+1][pos][w]
    2. f[t][pos][0] => f[t+1][pos'][0]
    3. f[t][pos][w] => f[t+1][pos'][0]
  • 其中第 3 种转移,需要经过一个中间点,我们可以对于 \(\forall x,y\) 预处理在 \(x\) 满油,在中间点加油,最终到达 \(y\) 最小代价。这里需要用到距离性质剪掉一些无效枚举来优化。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1002;
const int inf = 2e9 + 7;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);	
	int n; cin >> n;
	vector<int> a(n),b(n),c(n);
	for (int i = 0; i < n; i ++) cin >> a[i] >> b[i] >> c[i];
	int w, det; 
	cin >> w >> det;
	int s = 0, t = 1;

	vector<int> near[N];
	int d[N][N], d2[N][N];

	for (int i = 0; i < n; i ++) {
		for (int j = 0; j < n; j ++) { 
			d[i][j] = abs(a[i] - a[j]) + abs(b[i] - b[j]);
			if (d[i][j] <= w) near[i].push_back(j);
			d2[i][j] = inf;
		}
	}
	
	for (int i = 0; i < n; i ++) {
		for (int j = 0; j < n; j ++) if (d[i][j] > w && d[i][j] < 2 * w) {
			for (auto k: near[i]) {
				if (d[k][j] <= w) {
					d2[i][j] = min(d2[i][j], c[k] * (d[j][k] - (w - d[i][k])));
				}
			}
		}
	}
	/*
	for (int i = 0; i < n; i ++) {
		for (int j = 0; j < n; j ++) {
			printf("d[%d][%d] = %lld, d2[%d][%d] = %lld\n", i,j,d[i][j],i,j,d2[i][j]);
		}
	}
	*/
	const LL dpinf = 1e12;
	LL down[12][N], up[12][N];
	for (int i = 0; i < 12; i ++) for (int j = 0; j < N; j ++)
		down[i][j] = up[i][j] = dpinf;

	auto upd = [](LL &x, LL y) {
		x = min(x, y);
	};

	down[0][s] = 0;
	for (int i = 0; i <= det; i ++) {
		for (int x = 0; x < n; x ++) {
			//printf("down[%d][%d] = %lld, up[%d][%d] = %lld\n",i,x,down[i][x],i,x,up[i][x] );
			if (down[i][x] < dpinf) {
				upd(up[i+1][x], down[i][x] + w * c[x]);
			}	
			for (int y = 0; y < n; y ++) {
				if (d[x][y] <= w) {
					upd(down[i+1][y], down[i][x] + d[x][y] * c[x]);
					upd(up[i+1][y], up[i][x] + d[x][y] * c[y]);
				}
				if (d2[x][y] != inf) {
					upd(down[i+1][y], up[i][x] + d2[x][y]);
				}
			}
		}
	} 
	LL ans = dpinf;
	for (int i = 0; i <= det; i ++) 
		upd(ans, down[i][t]);
	if (ans == dpinf) ans = -1;

	cout << ans << endl;
}		

L. Two Buildings

决策单调性:\(p(j)\) 表示 \(j\) 为右端点,最优的左端点位置,这个函数是不降的。

分治即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1000000 + 10;

int a[N];
LL dp[N], ans = 0;
vector<int> vi, vj;
void solve(int l,int r,int L,int R) {
	int mid = (l+r)>>1;
	if (l > r) return;
	LL mx = 0; int pos = -1;
	for (int i = L; i <= R; i ++) {
		if (vi[i] >= vj[mid]) break;
		LL tmp = 1LL * (a[vi[i]] + a[vj[mid]]) * (vj[mid] - vi[i]);
		if (tmp >= mx) {
			pos = i, mx = tmp;
		}
	}
	if (pos == -1) return;
	ans = max(ans, mx);
	solve(l, mid - 1, L, pos);
	solve(mid + 1, r, pos, R);
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);	
	int n; cin >> n;
	for (int i = 0; i <= n; i ++) a[i] = dp[i] = 0;
	for (int i = 1; i <= n; i ++) cin >> a[i];
	int mx = 0;
	for (int i = 1; i <= n; i ++) {
		if (a[i] > mx) {
			mx = a[i]; vi.push_back(i);
		}
	}
	mx = 0;
	for (int i = n; i >= 1; i --) {
		if (a[i] > mx) {
			mx = a[i]; vj.push_back(i);
		}
	}
	reverse(vj.begin(), vj.end());
	solve(0, (int)vj.size() - 1, 0, (int)vi.size() - 1);
	cout << ans << endl;
}


posted @ 2021-02-01 17:46  FST_stay_night  阅读(280)  评论(0编辑  收藏  举报