NOIP2014提高组.飞扬的小鸟

题目

512. 飞扬的小鸟
NOIP2014提高组.飞扬的小鸟_第1张图片

思路

不难发现状态可以表示为 f [ i ] [ j ] f[i][j] f[i][j]也就是到达 ( i , j ) (i, j) (i,j)位置的最小点击数, 当前状态可以由上一个位置的状态转移, f [ i − 1 ] [ j + y ] f[i - 1][j + y] f[i1][j+y], 或者 f [ i − 1 ] [ j − k ⋅ x ] + k f[i - 1][j - k \cdot x] + k f[i1][jkx]+k转移, 计算时间复杂度 10000 × 1000 × 1000 = 1 0 10 10000 \times 1000 \times 1000 = 10 ^ {10} 10000×1000×1000=1010必定超时, 因此需要将状态转移方程做等价变形

原状态转移方程如下
f [ i ] [ j ] = min ⁡ ( f [ i − 1 ] [ j + y ] , f [ i − 1 ] [ j − k ⋅ x ] + k ) f[i][j] = \min (f[i - 1][j + y], f[i - 1][j - k \cdot x] + k) f[i][j]=min(f[i1][j+y],f[i1][jkx]+k)

将第二项展开得到

f [ i − 1 ] [ j − x ] + 1 , f [ i − 1 ] [ j − 2 x ] + 2 , . . . , f [ i − 1 ] [ j − k x ] + k f[i - 1][j - x] + 1, f[i - 1][j - 2x] + 2, ..., f[i - 1][j - kx] + k f[i1][jx]+1,f[i1][j2x]+2,...,f[i1][jkx]+k

再展开 j = j − x j = j - x j=jx的方程

f [ i ] [ j − x ] = min ⁡ ( f [ i − 1 ] [ j + y − x ] , f [ i − 1 ] [ j − 2 x ] + 1 , f [ i − 1 ] [ j − 3 x ] + 2 , . . . , f [ i − 1 ] [ j − k x ] + k ) f[i][j - x] = \min(f[i - 1][j + y - x], f[i - 1][j - 2x] + 1, f[i - 1][j - 3x] + 2, ..., f[i - 1][j - kx] + k) f[i][jx]=min(f[i1][j+yx],f[i1][j2x]+1,f[i1][j3x]+2,...,f[i1][jkx]+k)

注意到从第二项开始的部分和 f [ i ] [ j ] f[i][j] f[i][j]的第二项开始的部分类似, 可以记为 g [ i ] [ j ] g[i][j] g[i][j], 那么 g [ i ] [ j ] = min ⁡ ( g [ i ] [ j − x ] + 1 , f [ i − 1 ] [ j − x ] + 1 ) g[i][j] = \min (g[i][j - x] + 1, f[i - 1][j - x] + 1) g[i][j]=min(g[i][jx]+1,f[i1][jx]+1), 状态转移方程转化为 f [ i ] [ j ] = min ⁡ ( f [ i − 1 ] [ j + y ] , g [ i ] [ j ] ) f[i][j] = \min (f[i - 1][j + y], g[i][j]) f[i][j]=min(f[i1][j+y],g[i][j]), 空间换时间优化掉一维, 时间复杂度变为 1 0 7 10 ^ 7 107可以通过

原始版本代码

#include 
#include 
#include 

using namespace std;

const int N = 10010, M = 1010, INF = 0x3f3f3f3f;

int n, m, cnt;
int x[N], y[N];
int pos[N], d[N], u[N];
int f[N][M], g[N][M];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n >> m >> cnt;
	for (int i = 1; i <= n; ++i) cin >> x[i] >> y[i];
	for (int i = 1; i <= cnt; ++i) {
		int pos, a, b;
		cin >> pos >> a >> b;
		d[pos] = a, u[pos] = b;
	}

	memset(f, 0x3f, sizeof f);
	memset(g, 0x3f, sizeof g);
	for (int i = 1; i <= m; ++i) f[0][i] = 0;

	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			if (j > x[i]) g[i][j] = min(g[i][j - x[i]] + 1, f[i - 1][j - x[i]] + 1);
			if (!u[i] || j > d[i] && j < u[i]) {
				f[i][j] = g[i][j];
				if (j + y[i] <= m) f[i][j] = min(f[i][j], f[i - 1][j + y[i]]);
			}
		}

		if (!u[i]) {
			f[i][m] = min(f[i][m], f[i - 1][m] + 1);
			for (int j = 1; j < m; ++j) {
				f[i][m] = min(f[i][m], f[i - 1][j] + (m - j + x[i] - 1) / x[i]);
			}
		}
	}

	int res = INF;
	for (int i = 1; i <= m; ++i) res = min(res, f[n][i]);

	if (res < INF >> 1) {
		cout << 1 << "\n";
		cout << res << "\n";
		return 0;
	}

	cout << 0 << "\n";
	for (int i = n; i >= 0; --i) {
		for (int j = 1; j <= m; ++j) {
			if (f[i][j] < INF >> 1) {
				int ans = 0;
				for (int k = 0; k <= i; ++k) {
					if (u[k]) ans++;
				}
				cout << ans << "\n";
				return 0;
			}
		}
	}

	return 0;
}

优化版本代码

#include 
#include 
#include 

using namespace std;

const int N = 10010, M = 1010, INF = 0x3f3f3f3f;

int n, m, cnt;
int x[N], y[N];
int pos[N], d[N], u[N];
int f[N][M], g[M];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n >> m >> cnt;
	for (int i = 1; i <= n; ++i) cin >> x[i] >> y[i];
	for (int i = 1; i <= cnt; ++i) {
		int pos, a, b;
		cin >> pos >> a >> b;
		d[pos] = a, u[pos] = b;
	}

	memset(f, 0x3f, sizeof f);
	for (int i = 1; i <= m; ++i) f[0][i] = 0;

	for (int i = 1; i <= n; ++i) {
		memset(g, 0x3f, sizeof g);
		for (int j = 1; j <= m; ++j) {
			if (j > x[i]) g[j] = min(g[j - x[i]] + 1, f[i - 1][j - x[i]] + 1);
			if (!u[i] || j > d[i] && j < u[i]) {
				f[i][j] = g[j];
				if (j + y[i] <= m) f[i][j] = min(f[i][j], f[i - 1][j + y[i]]);
			}
		}

		if (!u[i]) {
			f[i][m] = min(f[i][m], f[i - 1][m] + 1);
			for (int j = 1; j < m; ++j) {
				f[i][m] = min(f[i][m], f[i - 1][j] + (m - j + x[i] - 1) / x[i]);
			}
		}
	}

	int res = INF;
	for (int i = 1; i <= m; ++i) res = min(res, f[n][i]);

	if (res < INF >> 1) {
		cout << 1 << "\n";
		cout << res << "\n";
		return 0;
	}

	cout << 0 << "\n";
	for (int i = n; i >= 0; --i) {
		for (int j = 1; j <= m; ++j) {
			if (f[i][j] < INF >> 1) {
				int ans = 0;
				for (int k = 0; k <= i; ++k) {
					if (u[k]) ans++;
				}
				cout << ans << "\n";
				return 0;
			}
		}
	}

	return 0;
}

你可能感兴趣的:(算法,c++,笔记)