「JSOI2014」打兔子

「JSOI2014」打兔子

传送门

首先要特判 \(k \ge \lceil \frac{n}{2} \rceil\) 的情况,因为此时显然可以消灭所有的兔子,也就是再环上隔一个点打一枪。

但是我们又会发现当 \(n = 3, k = 2\) 时,这种情况也满足上述条件但是我们只能打掉两群兔子,所以选兔子最多的两个格子打。

对于剩下的情况我们可以考虑 \(\text{DP}\) 。

我们可以发现一件事,就是说如果我们把环弱化成链,那么顺着打就可以包含所有状态了。

所以说我们就可以有一个性质:两个相邻的格子不会被同时打。

然后我们就在链上先跑 \(\text{DP}\) :设 \(dp_{i, j, 0 / 1}\) 表示在前 \(i\) 个格子中开了 \(j\) 枪,第 \(i\) 个格子有没有开枪的最大收益。

转移就是:

  • 第 \(i+1\) 个格子不开 : \(dp_{i + 1, j, 0} \leftarrow \max\{dp_{i, j, 0}, dp_{i, j, 1}\}\)
  • 第 \(i\) 个格子不开,第 \(i + 1\)个格子开:\(dp_{i + 1, j + 1, 1} \leftarrow dp_{i, j, 0} + a_{i + 1}\)
  • 第 \(i\) 个格子开,第 \(i + 1\) 个格子不开,第 \(i + 2\) 个格子开:\(dp_{i + 2, j + 1, 1} \leftarrow dp_{i, j, 0} + a_{i + 1} + a_{i + 2}\)

然后对于环的问题,我们就讨论一下第 \(1\) 个格子和第 \(n\) 个格子的开枪情况即可。

参考代码:

#include <algorithm>
#include <cstring>
#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < class T > void chkmax(T &a, const T& b) { a = a > b ? a : b; }
template < class T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while ('0' > c || c > '9') f |= c == '-', c = getchar();
    while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    s = f ? -s : s;
}

const int _ = 4010;

int n, k, a[_], dp[_][_][2];

inline void DP() {
    for (rg int i = 1; i < n; ++i)
        for (rg int j = 0; j <= k; ++j) {
            chkmax(dp[i + 1][j][0], max(dp[i][j][0], dp[i][j][1]));
            if (j + 1 <= k) chkmax(dp[i + 1][j + 1][1], dp[i][j][0] + a[i + 1]);
            if (j + 1 <= k && i + 2 <= n) chkmax(dp[i + 2][j + 1][1], dp[i][j][1] + a[i + 1] + a[i + 2]);
    }
}

inline int calc1() {
    memset(dp, 0xaf, sizeof dp), dp[1][0][0] = 0;
    DP();
    return dp[n][k][0];
}

inline int calc2() {
    int tmp = a[n]; a[n - 1] += tmp, a[n] = 0;
    memset(dp, 0xaf, sizeof dp), dp[1][1][1] = a[1], DP();
    a[n] = tmp, a[n - 1] -= tmp;
    return dp[n][k][0];
}

int main() {
#ifndef ONLINE_JUDGE
    file("cpp");
#endif
    read(n), read(k);
    for (rg int i = 1; i <= n; ++i) read(a[i]);
    int ans = 0;
    if (k >= (n + 1) / 2) {
        if (n == 3 && k == 2)
            sort(a + 1, a + n + 1), printf("%d\n", a[2] + a[3]);
        else {
            for (rg int i = 1; i <= n; ++i) ans += a[i];
            printf("%d\n", ans);
        }
        return 0;
    }
    chkmax(ans, calc1());
    chkmax(ans, calc2());
    reverse(a + 1, a + n + 1);
    chkmax(ans, calc2());
    printf("%d\n", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/zsbzsb/p/12263824.html

时间: 2024-08-30 15:02:01

「JSOI2014」打兔子的相关文章

「JSOI2014」序列维护

「JSOI2014」序列维护 传送门 其实这题就是luogu的模板线段树2,之所以要发题解就是因为被 \(\color{black}{\text{M}} \color{red}{\text{_sea}}\) 告知了一种比较NB的 \(\text{update}\) 的方式. 我们可以把修改操作统一化,视为 \(ax + b\) 的形式,然后我们按照原来的套路来维护两个标记,分别代表 \(a\) 和 \(b\) ,那么我们的更新就可以这么写: inline void f(int p, int at

「JSOI2014」强连通图

「JSOI2014」强连通图 传送门 第一问很显然就是最大的强连通分量的大小. 对于第二问,我们先把原图进行缩点,得到 \(\text{DAG}\) 后,统计出入度为零的点的个数和出度为零的点的个数,两者取 \(\max\) 就是答案. 理性证明可以看这里 参考代码: #include <cstdio> #define rg register #define file(x) freopen(x".in", "r", stdin), freopen(x&q

「JSOI2014」学生选课

「JSOI2014」学生选课 传送门 看到这题首先可以二分. 考虑对于当前的 \(mid\) 如何 \(\text{check}\) 我们用 \(f_{i,j}\) 来表示 \(i\) 对 \(j\) 的好感度排名,那么对于两个人 \(i\),\(j\) 如果有 \(\max\{f_{i, j}, f_{j, i}\} > mid\) 那么显然这两个人是不能上同一个老师的课的. 而且每个人可以上的课只有两种,我们记为 \(a_{i, 0 / 1}\) 假设 \(i\),\(j\) 对于当前的 \

「JSOI2014」矩形并

「JSOI2014」矩形并 传送门 我们首先考虑怎么算这个期望比较好. 我们不难发现每一个矩形要和 \(n - 1\) 个矩形去交,而总共又有 \(n\) 个矩形,所以我们把矩形两两之间的交全部加起来再除以 \(n(n - 1)\) 就是答案. 至于算矩形之间的交我们可以考虑把每个矩形都视为在这个矩形范围内区间加上 \(1\) ,那么我们只需要查询一个矩形内的和 - 该矩形自身的贡献就可以算出一个矩形与其他矩形的交. 所以现在相当于我们只需要实现二维的区间加/查询. 但是数据范围很大我们不可能用

「AHOI2014/JSOI2014」骑士游戏

「AHOI2014/JSOI2014」骑士游戏 传送门 考虑 \(\text{DP}\). 设 \(dp_i\) 表示灭种(雾)一只编号为 \(i\) 的怪物的代价. 那么转移显然是: \[dp_i = \min(K_i, S_i + \sum_{j = 1}^{R_i} dp_{v_j})\] 但是我们会发现这个东西是有后效性的... 所以我们会想要用建图然后跑一个最短路什么的来搞... 于是我们观察到上面那个 \(\text{DP}\) 式子中,\(dp_i\) 如果用后面那一项来转移,显然

「AHOI2014/JSOI2014」支线剧情

「AHOI2014/JSOI2014」支线剧情 传送门 上下界网络流. 以 \(1\) 号节点为源点 \(s\) ,新建一个汇点 \(t\),如果 \(u\) 能到 \(v\),那么连边 \(u \to v\),下界为 \(1\),上界为 \(+\infty\),费用为对应的所需时间,表示这段剧情至少看一次,且看一次代价为对应的所需时间. 又因为我们可以在任何一个节点重开一次,所以我们的每个节点 \(u\) 都连边 \(u \to t\) ,下界为 \(0\),上界为 \(+\infty\),费

「AHOI2014/JSOI2014」拼图

「AHOI2014/JSOI2014」拼图 传送门 看到 \(n \times m \le 10^5\) ,考虑根号分治. 对于 \(n < m\) 的情况,我们可以枚举最终矩形的上下边界 \(tp, bt\),那么我们发现最终矩形一定是由所有满足从第 \(tp\) 行到第 \(bt\) 行都是白格子的矩形顺次连接,并且两端再各自接上一个最大的前缀和一个最大的后缀构成的. 这个我们可以 \(O(m)\) 地算. 总复杂度就是 \(O(n^2m)\),也就是一个根号级别的. 对于 \(n \ge

「网络流」

$Tasklist$ 无限之环 星际竞速 4823: 老C的方块 2007: 海拔 还有51nod上的 集合交易 「奇怪的游戏」:小学数学+最大流 如果不相等,算出来要多叠多少层,否则答案具有二分性.check用最大流 土兵占领:补集转化+最小割 转化为最多有多少士兵能同时给一行和一列作出贡献,然后最小割可以做 「紧急疏散」:增量+最大流 必须根据时间拆点,而不能每经过一个时间给终点流量+1,因为不能让后面的人占用前面的流量 狼抓兔子:最小割/对偶图 不管写最小割还是对偶图都是模板 「切糕」:最

AC日记——「HNOI2017」单旋 LiBreOJ 2018

#2018. 「HNOI2017」单旋 思路: set+线段树: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #define maxtree maxn<<2 int val[maxtree],tag[maxtree],L[maxtree],R[maxtree],mid[maxtree]; int op[maxn],ki[maxn],bi[maxn],cnt,size,n,ch[maxn]