AtCoder Beginner Contest 236 (C-E)

C - Route Map

题意:给定n个S串和m个T串,对于每一个S串问是否其出现在T串中。

题解:map模拟记录T串,对于S,询问map中是否存在就行。

AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <string>
#include <vector>
#include <map>

using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
string s[N];
map<string, bool> h;

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
        cin >> s[i];
    
    string res;
    for (int i = 1; i <= m; i ++ )
    {
        cin >> res;
        h[res] = true;
    }
    for (int i = 1; i <= n; i ++ )
        if (h[s[i]]) puts("Yes");
        else puts("No");
    return 0;
}

D - Dance (dfs)

题意:有n个人,并给出两两配对的亲和度,找出所有匹配对的亲和度的异或最大值

题解:对于每个人,去寻找还未匹配到人的就行,第一个人可匹配的人数为(n - 1),第二个未匹配的人可匹配的人数为(n - 3),依次类推,因此复杂度为O((n - 1) (n - 3) * (n - 5)…*2),n最大是16,因此时间复杂度试2e6左右。

AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <string>
#include <vector>

using namespace std;
typedef long long ll;
const int N = 30;
int g[N][N];  // g[i][j]表示i和j匹配的亲和度
bool st[N];   // 用于标记此人是否匹配了
int ans = 0, n;


// cnt表示这是第几个人,res是当前及匹配的亲和度的异或值
void dfs(int cnt, int res)
{
    if (!cnt)    // 匹配完
    {
        ans = max(ans, res);
        return;
    }
    if (st[cnt])  // 这个人已经被匹配了
    {
        dfs(cnt - 1, res);
        return;
    }
    for (int i = 1; i <= n; i ++ )
    {
        if (i == cnt || st[i]) continue;
        st[cnt] = st[i] = true;
        dfs(cnt - 1, res ^ g[cnt][i]);
        st[cnt] = st[i] = false;   // 回溯,还原现场
    }
}

int main()
{    
    scanf("%d", &n);
    n *= 2;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
        {
            if (j <= i) continue;
            scanf("%d", &g[i][j]);
            g[j][i] = g[i][j];
        }
    
    dfs(n, 0);
    printf("%d\n", ans);
    return 0;
}

E - Average and Median (二分 + DP)

题意:给出n 个数a1…an,然后选取一些数出来,选取数的方式是对于每一个 i (1<=i<=n-1), 第i个数或第i+1个数至少选出来一个,问在所有的这些选法中的最大平均值和最大中位数是多少。

题解:二分答案用dp去check二分出来的值是否合法。

对于求最大的平均值:假设现在二分到的答案是mid,那么我们令b[i] = a[i] - mid,如果按题目给出的选法在b数组中选出的数的和至少有一组大于等于0说明真实答案一定不会比mid小,然后令l = mid, 所以我们只需求出最大的一组和就行。令f[i][0]表示从前i个数里面选且不选i的最大的和,f[i][1]表示从前i个数里面选且选i的最大的和,因此f[i][0] = f[i - 1][1](第i个不选,因此第i - 1个一定要选),f[i][1] = max(f[i - 1][0], f[i - 1][1]) + b[i](第i个选,因此第i - 1个可选可不选,找其中的最大值就行),若max(f[i][0], f[i][1]) >= 0, 说明选出来的方案中至少存在一组数的和大于等于0.

对于求最大的中位数:对于中位数我们可以发现大于等于中位数的数的数量一定是 大于 小于中位数的数的数量(有点拗口 ),所以只要我们能在a[i]中按题目的选法选出大于等于mid的数量 大于 小于mid的数量,说明真实答案一定不会比mid小,然后令l = mid。因此在这里, 如果a[i] >= mid,则令b[i] = 1, 否则令b[i] = -1,所以在b数组中选出一组数的和大于0即可,我们同样是找选出来的和最大值是否大于0即可,dp转移方程如上。

AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <string>
#include <vector>

using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int g[N];
int n;

bool check1(double x)
{
    vector<vector<double> > f(n + 1, vector<double>(2));
    f[0][0] = 0, f[0][1] = 0;
    for (int i = 1; i <= n; i ++ )
    {
        f[i][0] = f[i - 1][1];
        f[i][1] = max(f[i - 1][1], f[i - 1][0]) + g[i] - x;
    }
    // cout << x << " " << max(f[n][0], f[n][1]) << endl;
    return max(f[n][0], f[n][1]) >= 0;
}

bool check2(int x)
{
    vector<vector<int> > f(n + 1, vector<int>(2));

    for (int i = 1; i <= n; i ++ )
    {
        f[i][0] = f[i - 1][1];
        f[i][1] = max(f[i - 1][1], f[i - 1][0]) + (g[i] >= x ? 1 : -1);
    }
    return max(f[n][0], f[n][1]) > 0;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &g[i]);

    double l = 0, r = 1e9;
    while (r - l >= 1e-6)
    {
        double mid = (l + r) / 2;
        if (check1(mid)) l = mid;
        else r = mid;
    }
    
    int L = 0, R = 1e9;
    while (L < R)
    {
        int mid = L + R + 1 >> 1;
        if (check2(mid)) L = mid;
        else R = mid - 1;
    }
    cout << l << "\n" << L << endl;
    return 0;
}

完结,如有错误,还请指出,感谢!!!

上一篇:vue_记事本


下一篇:使用 CSS 去掉 iPhone 网页上按钮的超大圆角默认样式