GMOJ 2021.1.18模拟赛【NOIP2014day1】飞扬的小鸟

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

输入1:
10 10 6
3 9
9 9
1 2
1 3
1 2
1 1
2 1
2 1
1 6
2 2
1 2 7
5 1 5
6 3 5
7 5 8
8 7 9
9 1 3
输入2:
10 10 4
1 2
3 1
2 2
1 8
1 8
3 2
2 1
2 1
2 2
1 2
1 0 2
6 7 9
9 1 4
3 8 10

Sample Output

输出1:
1
6
输出2:
0
3

Data Constraint

在这里插入图片描述

题解

70pts:
不难想到此题使用DP 设 f i , j f_{i,j} fi,j表示到第i列第j行的答案,则对于一般的j状态转移方程为 f i , j = m i n ( f i − 1 , j + y i − 1 , m i n j − k ∗ x i − 1 > 0 , k > 0 ( f i − 1 , j − k ∗ x i − 1 ) + k ) f_{i,j} = min(f_{i - 1, j + y_{i - 1}}, min_{j - k * x_{i - 1} > 0,k>0} (f_{i - 1, j - k * x_{i - 1}}) + k) fi,j=min(fi1,j+yi1,minjkxi1>0,k>0(fi1,jkxi1)+k)
然后特判下边界就行~
复杂度 O ( n m 2 ) O(nm^2) O(nm2)
100pts
注意到上式中的 m i n j − k ∗ x i − 1 > 0 , k > 0 ( f i − 1 , j − k ∗ x i − 1 ) min_{j - k * x_{i - 1} > 0,k>0} (f_{i - 1, j - k * x_{i - 1}}) minjkxi1>0,k>0(fi1,jkxi1) m i n j − k ∗ x i − 1 > 0 , k > 1 ( f i − 1 , j − k ∗ x i − 1 ) min_{j - k * x_{i - 1} > 0 , k > 1} (f_{i - 1, j - k * x_{i - 1}}) minjkxi1>0,k>1(fi1,jkxi1)都被计算过,直接拿来用就行了,于是方程化为 f i , j = m i n ( f i − 1 , j − x i − 1 , f i , j − x i − 1 ) + 1 f_{i,j} = min(f_{i - 1, j - x_{i - 1}}, f_{i, j - x_{i - 1}}) + 1 fi,j=min(fi1,jxi1,fi,jxi1)+1 ,每做完一列再做一次 f i , j = m i n ( f i , j , f i − 1 , j + y i − 1 ) f_{i,j} = min(f_{i, j}, f_{i - 1, j + y_{i - 1}}) fi,j=min(fi,j,fi1,j+yi1),于是复杂度化为 O ( n m ) O(nm) O(nm)可以通过本题。

具体实现要注意下细节,我的代码可能打的不是太优美,但是可以拿来参考

Code

#include <cstdio>
#include <iostream>
using namespace std;
const int N = 10100;
const int M = 2000;
const int inf = 9999999;
int n, m, k, x[N], y[N], l[N], h[N], ans = 9999999, f[N][M], cnt;
inline int read() {
	int res = 0;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar());
	for(; ch >= '0' && ch <= '9'; res = res * 10 + ch - '0', ch = getchar());
	return res;
}
int main() {
	n = read(), m = read(), k = read();
	for(int i = 1; i <= n; ++i) {
		x[i] = read(),
		y[i] = read(),
		l[i] = m + 1,
		h[i] = 0;
	}
	l[n + 1] = m + 1, h[n + 1] = 0;
	for(int i = 1; i <= k; ++i) {
		int p = read(), lo = read(), hi = read();
		h[p + 1] = lo,
		l[p + 1] = hi;
	}
	for(int i = 1; i <= n + 1; ++i)
		for(int j = 0; j < M; ++j)
			f[i][j] = inf;
	for(int i = h[1] + 1; i < l[1]; ++i) 
		f[1][i] = 0; 
	for(int i = 2; i <= n + 1; ++i) {
		bool flag = false;
		for(int j = x[i - 1] + 1; j < m; ++j) 
			f[i][j] = min(f[i - 1][j - x[i - 1]], f[i][j - x[i - 1]]) + 1;
		for(int j = 1; j <= m; ++j)
			f[i][m] = min(f[i][m], f[i - 1][j] + max(1, (m - j + x[i - 1] - 1) / x[i - 1]));
		for(int j = 1; j < m; ++j) {
			f[i][j] = min(f[i][j], f[i - 1][j + y[i - 1]]);
			if(f[i][j] == inf + 1) --f[i][j];
		}
		if(f[i][m] > inf) f[i][m] = inf;
		for(int j = 0; j <= h[i]; ++j) f[i][j] = inf;
		for(int j = l[i]; j <= m; ++j) f[i][j] = inf;
		for(int j = h[i] + 1; j < l[i]; ++j)
			if(f[i][j] != inf) flag = true;
		if(!flag) {
			printf("0\n%d\n",cnt);
			return 0;
		}
		if(l[i] != m + 1 || h[i] != 0) ++cnt;
	}
	for(int i = h[n + 1] + 1; i < l[n + 1]; ++i)
		ans = min(ans, f[n + 1][i]);
	printf("1\n%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值