概述

没啥难题,也没有什么特别推荐的题。(快来做前几天的题!!)

A

Description

有四个房间,每个房间有两个守卫,每个守卫可以用不小于一给定价钱的巧克力或果汁贿赂。

现在你有 $n$ 元要恰好花完,要贿赂一个房间里的两个守卫,问怎么分配钱。

Solution

关键在于读懂英文题面,转化成上面的意思,然后就做完了。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::cerr;

int n;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        int x = std::min(a, b), y = std::min(c, d);
        if (x + y <= n)
        {
            cout << i << " " << x << " " << n - x << endl;
            return 0;
        }
    }
    cout << -1 << endl;
}

B

Description

有 $n$ 个任务,你每次可以做连续的 $k$ 个,做完这 $k$ 个之后会接着做连续的 $k$ 个任务。一直做到不能做结束。你每次开始做任务时会得到一个对应的怒气值,而连续做的任务不会得到怒气值。

问从哪个任务开始做得到怒气值最小。(建议看英文题面)

Solution

发现从第 $i$ 个任务开始做,就会得到任务 $i,i+k,i+2k…$ 的怒气值。也就是说任务被分成了 $k$ 组。

没啥可说的,直接预处理每组任务怒气值然后输出最小值就可以了。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::cerr;

const int maxn = 1e5 + 10;

int a[maxn], n, k;
int sum[maxn];

int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= k; i++)
    {
        for (int j = i; j <= n; j += k)
            sum[i] += a[j];
    }
    int ans = 0;
    sum[0] = 1 << 30;
    for (int i = 1; i <= k; i++)
        if (sum[i] < sum[ans])
            ans = i;
    cout << ans << endl;
}

C

Description

有 $n$ 个水果,每个水果有两个属性:美味值 $a$ 和卡路里值 $b$ 。现在选用若干个(至少 $1$ 个)水果制作一份特殊的沙拉,沙拉的美味值为所选的水果的美味值的和,沙拉的卡路里值为所选水果的卡路里值的和。沙拉的美味值恰好是卡路里值的 $k$ 倍。请计算该沙拉美味值最大为多少。

$n,a_i,b_i\leq100,k\leq10$

Solution

把卡路里值乘一下 $k$,问题转化为美味值和卡路里相等。

这个东西就非常背包。

随便乱写一个 DP:$f(i,j,k)$ 表示前 $i$ 个水果,美味值和为 $j$,卡路里和为 $k$ 的最大美味值。然后 $f(i,j,k)=\max(f(i-1,j,k),f(i-1,j-a_i,k-b_i)+a_i)$。这玩意稍微算一下就知道过不去。

因为转移已经是 $O(1)$ 的了,只能从状态入手。发现 $j,k$ 的转移都只和 $i$ 有关,可以搞成一维(类似emiya家今天的饭):$f(i,d)$ 表示前 $i$ 个水果,卡路里与美味值差为 $d$ 的最大美味值。然后 $f(i,d)=\max(f(i-1,d),f(i-1,d-(b_i-a_i))$。

这样就解决了。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::cerr;

const int maxn = 110, maxm = 3e5 + 10;

int f[maxm], a[maxn], b[maxn], n, k;

int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= n; i++)
        cin >> b[i], b[i] *= k;
    const int M = 1e5, D = 2e5;
    memset(f, -1, sizeof(f));
    f[D] = 0;
    for (int i = 1; i <= n; i++)
    {
        int d = b[i] - a[i];
        if (d > 0)
        {
            for (int j = -M; j + d <= M; j++)
            {
                if (f[j + D + d] != -1)
                    f[j + D] = std::max(f[j + D], f[j + d + D] + a[i]);
            }
        }
        else
        {
            for (int j = M; j + d >= -M; j--)
                if (f[j + D + d] != -1)
                    f[j + D] = std::max(f[j + D], f[j + d + D] + a[i]);
        }
    }
    cout << (f[D] == 0 ? -1 : f[D]) << endl;
}

D

Description

给出一个 $n$ 个点,$m$ 条边的无向图,每条边有区间 $\left[l_i,r_i\right]$,求从 $1$ 到 $n$ 路径组成的边集,使该边集中所有区间的交内的正整数元素个数最多。

$2\leq n\leq10^3,0\leq m\leq3\times10^3$。

Solution

数据范围这么小,我们直接枚举。

考虑枚举一个区间的左端点,然后二分找最大的右端点,再 check 一下图是否联通即可。

时间复杂度 $O(mn\log m)$。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::cerr;

const int maxn = 3e3 + 10;

struct UFS
{
    int father[maxn], siz[maxn];
    void init(int n = 1e3)
    {
        for (int i = 1; i <= n; i++)
            father[i] = i, siz[i] = 1;
    }
    int find(int x) { return x == father[x] ? x : father[x] = find(father[x]); }
    void uni(int x, int y)
    {
        int xx = find(x), yy = find(y);
        if (siz[xx] > siz[yy])
            std::swap(xx, yy);
        father[xx] = find(yy), siz[yy] += siz[xx];
    }
    bool ask(int x, int y) { return find(x) == find(y); }
} ufs;

struct edge
{
    int x, y, l, r;
    bool operator<(const edge &b) const { return r < b.r; }
} e[maxn];

int n, m;

bool check(int x, int y)
{
    ufs.init(n);
    for (int i = 1; i <= m; i++)
        if (e[i].l <= x && e[i].r >= y)
            ufs.uni(e[i].x, e[i].y);
    return ufs.ask(1, n);
}

int solve(int x)
{
    int l = 1, r = m, ans = 0;
    while (l <= r)
    {
        int mid = (l + r) / 2;
        if (check(x, e[mid].r))
            ans = mid, l = mid + 1;
        else
            r = mid - 1;
    }
    //cout << x << " " << e[ans].r << endl;
    return e[ans].r - x + 1;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
        scanf("%d%d%d%d", &e[i].x, &e[i].y, &e[i].l, &e[i].r);
    std::sort(e + 1, e + 1 + m);
    int ans = 0;
    for (int i = 1; i <= m; i++)
        ans = std::max(solve(e[i].l), ans);
    cout << (ans == 0 ? "Nice work, Dima!" : std::to_string(ans)) << endl;
}

E

Description

给你一个 $n\times m$ 矩阵。矩阵中有 $k$ 种元素。给你一个序列,你要将序列中的每一个元素转化为矩阵上的对应数字的坐标,并使得相邻元素表示的坐标的曼哈顿距离中的最大值最大,求出这个最大值。(建议看原题面)。

$n,m\leq2000$。$k\leq9$。

Solution

我们考虑算出每两种元素之间最大曼哈顿距离。

有一个比较套路的做法:把曼哈顿距离展开: $$ dis=\begin{cases}(x_1+y_1)-(x_2+y_2)\\(x_1-y_1)-(x_2-y_2)\\-(x_1-y_1)+(x_2-y_2)\\-(x_1+y_1)+(x_2+y_2)\end{cases} $$ 也就是说最大曼哈顿距离一定是这四种中的一个,我们对每个颜色维护 $x_1+y_1,x_1-y_1$ 的最大值和最小值然后在上面四种情况里取最大值即可。

Code

#include <iostream>
#include <queue>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::cerr;

const int maxn = 2010;
//const int dx[] = {0, 0, 1, -1};
//const int dy[] = {1, -1, 0, 0};

int ans[10][10], a[maxn][maxn];
int mxadd[10], miadd[10], mxmus[10], mimus[10], q[maxn], n, m, k, s;

inline void upd(int &x, int y) { x = std::max(x, y); }
inline void upd2(int &x, int y) { x = std::min(x, y); }

struct point
{
    int x, y;
};

int main()
{
    scanf("%d%d%d%d", &n, &m, &k, &s);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            scanf("%d", &a[i][j]);
    memset(mimus, 1, sizeof(mimus)), memset(miadd, 1, sizeof(miadd));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
        {
            upd(mxadd[a[i][j]], i + j);
            upd2(miadd[a[i][j]], i + j);
            upd(mxmus[a[i][j]], i - j);
            upd2(mimus[a[i][j]], i - j);
        }
    for (int i = 1; i <= k; i++)
        for (int j = 1; j <= k; j++)
        {
            ans[i][j] = std::max(std::max(abs(mxadd[i] - miadd[j]), abs(mxadd[j] - miadd[i])), 
                    std::max(abs(mxmus[i] - mimus[j]), abs(mxmus[j] - mimus[i])));
        }
    int res = 0;
    for (int i = 1; i <= s; i++)
        scanf("%d", &q[i]);
    for (int i = 1; i < s; i++)
        res = std::max(res, ans[q[i]][q[i + 1]]);
    cout << res << endl;
}