C - TT 的奖励
捡猫咪游戏是这样的,猫咪从天上往下掉,且只会掉在 [0, 10] 范围内,具体的坐标范围如下图所示。
TT 初始站在位置五上,且每秒只能在移动不超过一米的范围内接住掉落的猫咪,如果没有接住,猫咪就会跑掉。例如,在刚开始的一秒内,TT 只能接到四、五、六这三个位置其中一个位置的猫咪。
喜爱猫咪的 TT 想要接住尽可能多的猫咪,你能帮帮他吗?
Input
多组样例。每组样例输入一个 m (0 < m < 100000),表示有 m 只猫咪。
在接下来的 m 行中,每行有两个整数 a b (0 < b < 100000),表示在第 b 秒的时候有一只猫咪掉落在 a 点上。
注意,同一个点上同一秒可能掉落多只猫咪。m = 0 时输入结束。
Output
输出一个整数 x,表示 TT 可能接住的最多的猫咪数。
Sample Input
6
5 1
4 1
6 1
7 2
7 2
8 3
0
Sample Output
4
思路:dp[i][j]表示第i秒站在j位置能获得最多🐱。显然是一个dp问题。
最先必须在特定位置开始,逆序遍历。
dp[i][j]=max(dp[i-1][k])
#include <iostream>
#include <algorithm>
using namespace std;
int dp[11][100001];
int main()
{
int m;
while (cin >> m)
{
memset(dp, 0, sizeof(dp));
if (m == 0) break;
int maxB = 0;
for (int i = 0; i < m; i++)
{
int a, b; cin >> a >> b;
dp[a][b]++;
maxB = max(maxB, b);
}
for (int i = maxB - 1; i >= 0; i--)
{
for (int j = 0; j <= 10; j++)
{
int tmp = dp[j][i + 1];
if (j > 0) tmp = max(tmp, dp[j - 1][i + 1]);
if (j < 10) tmp = max(tmp, dp[j + 1][i + 1]);
dp[j][i] += tmp;
}
}
cout << dp[5][0] << endl;
}
return 0;
}
D - TT 的苹果树
在大家的三连助攻下,TT 一举获得了超级多的猫咪,因此决定开一间猫咖,一年后,苹果熟了,到了该摘苹果的日子了。
已知树上共有 N 个节点,每个节点对应一个快乐值为 w[i] 的苹果,为了可持续发展,TT 要求摘了某个苹果后,不能摘它父节点处的苹果。
Input
结点按 1~N 编号。
第一行为 N (1 ≤ N ≤ 6000) ,代表结点个数。
接下来 N 行分别代表每个结点上苹果的快乐值 w[i](-128 ≤ w[i] ≤ 127)。
接下来 N-1 行,每行两个数 L K,代表 K 是 L 的一个父节点。
输入有多组,以 0 0 结束。
Output
每组数据输出一个整数,代表所选苹果快乐值总和的最大值。
输入样例
7
1
1
1
1
1
1
1
1 3
7 4
2 3
4 5
6 4
3 5
0 0
输出样例
5
思路:树的边把树分成大树和子树两部分,问题就可以甩锅给子树
最大收益=本树收益+子树最大收益
要分成当前🍎摘不摘两种情况
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 6000 + 5;
int fa[MAXN], happy[MAXN], n, dp[MAXN][2], root;
vector<int> v[MAXN];
pair<int, int> solve(int r)
{
int c = 0, b = 0, sz = v[r].size();
for (int i = 0; i < sz; i++)
{
pair<int, int> tmp = solve(v[r][i]);
b += max(tmp.first, tmp.second);
c += tmp.first;
}
return make_pair(b, c + happy[r]);
}
int main()
{
while (cin >> n)
{
if (n == 0) break;
memset(fa, 0, sizeof(fa));
for (int i = 1; i <= n; i++) cin >> happy[i];
for (int i = 0; i < n - 1; i++)
{
int x, y; cin >> x >> y;
v[y].push_back(x);
fa[x] = y;
}
for (int i = 1; i <= n; i++)
if (fa[i] == 0) root = i;
pair<int, int> tmp = solve(root);
cout << max(tmp.first, tmp.second) << endl;
}
return 0;
}
E - TT 的神秘任务3
给定一个环,A[1], A[2], A[3], … , A[n],其中 A[1] 的左边是 A[n]。要求从环上找出一段长度不超过 K 的连续序列,使其和最大。
Input
第一行一个整数 T,表示数据组数,不超过 100。
每组数据第一行给定两个整数 N K。(1 ≤ N ≤ 100000, 1 ≤ K ≤ N)
接下来一行,给出 N 个整数。(-1000 ≤ A[i] ≤ 1000)。
Output
对于每一组数据,输出满足条件的最大连续和以及起始位置和终止位置。
如果有多个结果,输出起始位置最小的,如果还是有多组结果,输出长度最短的。
Sample Input
4
6 3
6 -1 2 -6 5 -5
6 4
6 -1 2 -6 5 -5
6 3
-1 2 -6 5 -5 6
6 6
-1 -1 -1 -1 -1 -1
Sample Output
7 1 3
7 1 3
7 6 2
-1 1 1
思路:
- 求和问题,用前缀和。
- 一段范围内的最值问题,用滑动窗口,单调队列
- 环,加一段绕回来的即可
- 必须至少要选一个,ans=0特殊处理
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 1e5 + 5;
int a[MAXN], sum[2 * MAXN], ans, l, r;
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int T, N, K;
cin >> T;
while (T--)
{
cin >> N >> K;
ans = 0;
for (int i = 1; i <= N; i++)
{
cin >> a[i];
sum[i] = a[i] + sum[i - 1];
}
for (int i = N + 1; i < 2 * N; i++)
sum[i] = sum[i - 1] + a[i - N];
deque<int> q;
q.push_back(0);
for (int i = 1; i <= 2 * N - 1; i++)
{
while (q.size() && sum[q.back()] > sum[i])
q.pop_back();
q.push_back(i);
while (q.size() && i - q.front() > K)
q.pop_front();
if (ans < sum[i] - sum[q.front()]
|| (ans == sum[i] - sum[q.front()] && (i - q.front()) < (r - 1 + 1)))
{
ans = sum[i] - sum[q.front()];
l = q.front() + 1;
r = i;
}
}
if (ans == 0)
{
ans = a[1];
l = r = 1;
}
l = l > N ? l - N : l;
r = r > N ? r - N : r;
cout << ans << ' ' << l << ' ' << r << endl;
}
}