浏览器标题切换
浏览器标题切换end
把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

LGOJP1941 飞扬的小鸟

题目链接

题目链接

题解

\(f[i][j]\)表示位置\((i,j)\)到达需要的最小点击数。
\(f[i][j]=\min\{{f[i-1][j-kx]+k},f[i-1][j+y]\}\)
\(O(nm^2)\)
考虑优化,首先\(f\)可以滚动,然后考虑优化掉一个\(m\)
\(g[j]\)表示\((i-1,j)\)往下每\(x_i\)个单位的\(\min\)。显然有\(g[j]=\min\{f[i][j],g[j-x_{i}]+1\}\)
那么则有\(f[i][j]=\min\{g[j-x_i]+1,f[i-1][j+y]\}\)。复杂度\(O(nm)\)
有个需要注意的点是到了跳到m就不能再高了,所以对于\(\{(i,j)|j+x_i>m\}\)的点没办法利用g\(O(1)\)转移(共有m-j个合法可转移点),这个可以通过对g维护一个后缀\(\min\)解决。
好像也可以类比01背包和完全背包来转移。不过我想的时候没想到那个方向QAQ。

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

const int inf = 0x3f3f3f3f;
const int N = 10010;
int n, m, k, f[2][1010], g[1010], x[N], y[N];
int p[N][2];

int main() {
	scanf("%d%d%d", &n, &m, &k);
	for(int i = 0; i < n; ++i) 
		scanf("%d%d", &x[i], &y[i]), p[i][0] = 0, p[i][1] = m + 1;
	p[n][0] = 0; p[n][1] = m + 1;
	for(int i = 1; i <= k; ++i) {
		int px, l, h;
		scanf("%d%d%d", &px, &l, &h);
		p[px][0] = l; p[px][1] = h;
	}
	int cur = 0, to = 0, min_val = inf;
	for(int i = 1; i <= n; ++i) {
		cur ^= 1;
		for(int j = 1; j <= m; ++j) f[cur][j] = inf; 
		
		for(int j = 1; j <= m; ++j) {
			if(j <= p[i][0] || j >= p[i][1]) continue;
			
			if(j == m) f[cur][j] = min(f[cur][j], min_val + 1); 
			if(j - x[i - 1] > 0 && j - x[i - 1] > p[i - 1][0]) 
				f[cur][j] = min(f[cur][j], g[j - x[i - 1]] + 1);
			if(j + y[i - 1] <= m && j + y[i - 1] < p[i - 1][1]) 
				f[cur][j] = min(f[cur][j], f[cur ^ 1][j + y[i - 1]]);
			
			if(f[cur][j] != inf) to = i;
		}

		for(int j = 1; j <= m; ++j) g[j] = inf; 
		min_val = inf;
		for(int j = 1; j <= m; ++j) {
			g[j] = f[cur][j];
			if(j - x[i] > 0) g[j] = min(g[j], g[j - x[i]] + 1);
			if(j + x[i] >= m) 
				min_val = min(min_val, g[j]);
		}
	}
	if(to == n) {
		int ans = inf;
		for(int j = 1; j <= m; ++j) ans = min(ans, f[cur][j]);
		printf("1\n%d\n", ans);
	} else {
		int tot = 0;
		for(int i = 0; i <= to; ++i) {
			if(!(p[i][0] == 0 && p[i][1] == m + 1)) ++tot;
		}
		printf("0\n%d\n", tot);
	}
	return 0;
}
posted @ 2019-08-07 16:26  henry_y  阅读(190)  评论(0编辑  收藏  举报