小奇挖矿2

小奇挖矿2 - 题解

题目大意

现在有 m+1 个星球,从左到右标号为 0m,小奇最初在 0 号星球。
n 处矿体,第 i 处矿体有 ai 单位原矿,在第 bi 个星球上。
由于飞船使用的是老式的跳跃引擎,每次它只能从第 x 号星球移动到第 x+4 号星球或 x+7 号星球。每到一个星球,小奇会采走该星球上所有的原矿,求小奇能采到的最大原矿数量。
注意,小奇不必最终到达 m 号星球。

大概思路

Part 1 (20 pts)

既然第一个条件给了 n=1,所以只需要判断这个点会不会被走到就行了。

判断函数:

bool check(int n) {
	for (int i = 1; i * 7 <= n; i ++) {
    	if ((n - i * 7) % 4 == 0) {
    		return 1;
    	}
    } return 0;
}

Part 2 (40 pts)

详情 说实话,我真的不会。跳过(慌

Part 3 (60 pts)

我一看,m105。这不就是妥妥的 dp 嘛。

代码:

#include <bits/stdc++.h>
#define Maxn 100005

using namespace std;

int n, m, a[Maxn], b[Maxn], dp[Maxn], maxl;

bool check(int x) {
	for (int i = 0; i <= x / 7; i ++) {
		if ((x - i * 7) % 4 == 0) {
			return 1;
		}
	} return 0;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> n >> m;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i] >> b[i];
		if (check(b[i])) { // 切勿漏掉
			dp[b[i]] += a[i];
		}
	}
	
	for (int i = 0; i <= m; i ++) {
		int now = dp[i];
		if (i >= 4) {
			dp[i] = max(dp[i], now + dp[i - 4]);
		} if (i >= 7) {
			dp[i] = max(dp[i], now + dp[i - 7]);
		} maxl = max(maxl, dp[i]);
	} // 纯纯dp
	
	cout << maxl;
	return 0;
}

Part 4 (70 pts)

枚举后可以知道,点 x 不能走到的点 pxpx(x+1),(x+2),(x+3),(x+5),(x+6),(x+9),(x+10),(x+13),(x+17)

所以我们得到了一个更快的 check 函数,而且不仅仅适用于起点为 0 了。

新版 check 函数:

bool check(int x, int y) {
	if (x == y + 1 || x == y + 2 || x == y + 3 ||
		x == y + 5 || x == y + 6 || x == y + 9 ||
		x == y + 10 || x == y + 13 || x == y + 17) {
		return 0;
	} return 1;
} // 判断y那能否走到x

容易得到:

dpi=min1j<i(dpj+wi)

根据新的递推式可以得到代码:

#include <bits/stdc++.h>
#define Maxn 100005

using namespace std;

int n, m, tot = 0, dp[Maxn], maxl;
struct Point {
	int x, num;
} a[Maxn], p[Maxn];

bool check(int x, int y) {
	if (x == y + 1 || x == y + 2 || x == y + 3 ||
		x == y + 5 || x == y + 6 || x == y + 9 ||
		x == y + 10 || x == y + 13 || x == y + 17) {
		return 0;
	} return 1;
}

bool cmp(Point a, Point b) {
	return a.x <= b.x;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> n >> m;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i].num >> a[i].x;
	} sort(a + 1, a + n + 1, cmp);
	
	for (int i = 1; i <= n; i ++) {
		if (check(a[i].x, 0)) {
			if (a[i].x == p[tot].x) {
				p[tot].num += a[i].num;
			} else {
				tot ++, p[tot] = a[i];
			}
		}
	}
	
//	for (int i = 1; i <= tot; i ++) {
//		cout << p[i].x << " " << p[i].num << "\n";
//	}
	
	for (int i = 1; i <= tot; i ++) {
		dp[i] = p[i].num;
		for (int j = 1; j < i; j ++) {
			if (check(p[i].x, p[j].x)) {
//				cout << i << " " << j << " : " << dp[i] << " " << dp[j] << " " << p[i].num << " ";
				dp[i] = max(dp[i], dp[j] + p[i].num);
//				cout << dp[i] << "\n";
			}
		} maxl = max(maxl, dp[i]);
	}
	
	cout << maxl;
	return 0;
}

吐槽:将近一倍的脑细胞消耗量多换来了 10pts(不值。这边认为考试的时候可以把 60pts 拿到就很可以了。

Part 5 (100 pts)

终于迎来了正解。

我们发现,Part 4 的时间复杂度为 O(n2)。所以只有 70pts​。

观察递推式,发现有一项是不变的,所以有如下推导:

dpi=min1j<i(dpj+wi)min1j<i(dpj)+wi

可以联想到单调队列优化DP和优先队列优化DP。我这里写的是优先队列优化DP。

#include <bits/stdc++.h>
#define Maxn 100005

using namespace std;

int n, m, tot = 0, dp, maxl;
struct Point {
	int x, num;
	bool operator <(const Point a) const{
		return num < a.num;
	}
} a[Maxn], p[Maxn], tmp[Maxn], t;
int del = 0;
priority_queue<Point> q;

bool check(int x, int y) {
	if (x == y + 1 || x == y + 2 || x == y + 3 ||
		x == y + 5 || x == y + 6 || x == y + 9 ||
		x == y + 10 || x == y + 13 || x == y + 17) {
		return 0;
	} return 1;
}

bool cmp(Point a, Point b) {
	return a.x <= b.x;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> n >> m;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i].num >> a[i].x;
	} sort(a + 1, a + n + 1, cmp);
	
	for (int i = 1; i <= n; i ++) {
		if (check(a[i].x, 0)) {
			if (a[i].x == p[tot].x) {
				p[tot].num += a[i].num;
			} else {
				tot ++, p[tot] = a[i];
			}
		}
	}
	
//	for (int i = 1; i <= tot; i ++) {
//		cout << p[i].x << " " << p[i].num << "\n";
//	}
	
	tmp[0].num = tmp[0].x = 0;
	q.push(tmp[0]);
	for (int i = 1; i <= tot; i ++) {
		dp = del = 0;
		while (!q.empty()) {
			t = q.top(), q.pop();
			tmp[++ del] = t;
			if (check(p[i].x, t.x)) {
				dp = t.num + p[i].num;
				break;
			}
		}
		
		for (int j = 1; j <= del; j ++) {
			q.push(tmp[j]);
		} // 补充点的信息
		
		tmp[0].num = dp, tmp[0].x = p[i].x;
		q.push(tmp[0]); // 新增点的信息
	}
	
	cout << q.top().num;
	return 0;
}

吐槽:谁家良心考试第一题这么变态。

此篇结。

posted @   BLM-dolphin  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示