Codeforces Round #494(Div.3)

Codeforces Round #494(Div.3)

A. Polycarp's Pockets

题意:

  • 一个人有n个硬币,面值为\(a_i\),他打算把他分到几个口袋当中。
  • 每个口袋中不可以有相同的硬币。
  • 问最少需要多少口袋。
  • 数据范围:\(a_i\leq 100\)

思路:

  • 求这n个序列中出现次数最多的硬币即可。
#include
using namespace std;
int n, vis[100+10];
int main()
{
    cin >> n;
    for(int i = 1, x; i <= n; i++)
    {
        scanf("%d", &x);
        vis[x]++;
    } int ans = 0;
    for(int i = 1; i <= 100+2; i++)
        ans = max(ans, vis[i]);
    cout << ans << endl;
    return 0;
}

B. Binary String Constructing

题意:

  • 你有三个数字\(a,b,x\)
  • 你需要构造一个字符串长度为\(n=a+b\),字符串中有\(a\)\(0\)\(b\)\(1\)
  • 定义特殊对为:如果两个相邻的位置字符不同,则为一个特殊对。
  • 要求构造的字符串也要有\(x\)个特殊对。

思路:

  • 可以先输出\(\frac{x}{2}\)对0和1,那么这样就能提供\(\frac{x}{2}*2-1\)个符合题目的特殊对。
  • 之后将剩余的0和剩余的1连续输出就行。
  • 当然还是要卡一些细节的。
  • 同时还要注意a和b的大小,因为如果0的数目更多一些,最好把0放在前面,因为如果把1放在前面的话,1可能不够用。
#include
using namespace std;
int main()
{
    int a, b, x;
    cin >> a >> b >> x;
    string ans;
    char u, v;
    if(a > b) u = '0', v = '1';
    else u = '1', v = '0', swap(a, b);
    for(int i = 1; i <= x/2; i++)
        ans += u, ans += v, a--, b--;
    if(x % 2 == 0)
    {
        while(b--) ans += v;
        while(a--) ans += u;
    }
    else
    {
        while(a--) ans += u;
        while(b--) ans += v;
    }
    cout << ans << endl;
}

C. Intense Heat

题意:

  • 求长度大于等于k的连续子段的平均值的最大值。
  • 数据范围:\(n\leq 5000\)

思路:

  • 暴力枚举序列长度从k到n。
  • 同时枚举区间的右端点,利用前缀和\(O(1)\)求值后计算。
#include
using namespace std;
const int maxn = 5e3 + 10;
double n, k, a[maxn];
//输出15位小数
int main()
{
    double ans = 0;
    cin >> n >> k;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        a[i] = a[i-1] + a[i];
    }
    //枚举长度
    for(int i = k; i <= n; i++)
        //枚举右端点
        for(int j = i; j <= n; j++)
        ans = max(ans, (a[j]-a[j-i])/i);
    printf("%.15f\n", ans);
    return 0;
}

D. Coins and Queries

题意:

  • 有n枚硬币,每一枚硬币价值为\(a_i\),所有价值都是2的整数次幂。
  • 有q次询问,每次给出一个数字问是否能从这n枚硬币的子集中选出一个来组成这个面值的数字。
  • 求这个子集最小是多少,如果无法得到这个面值,输出-1。

思路:

  • 我们知道任何一个数字都可以表示成二进制的形式,所以可以从这个方向下手。
  • 因为硬币是2的整数次幂,所以我们先预处理出来每个位置上的数目,然后每当我们要凑钱的时候就从高位往低位配。
  • 算是贪心。
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
int n, q;
int sum[35];

int main()
{
    scanf("%d%d", &n, &q);
    for(int i = 1, x; i <= n; i++)
    {
        scanf("%d", &x);
        int t = log2(x);
        sum[t]++;
    }
    while(q--)
    {
        int x, ans = 0;
        scanf("%d", &x);
        for(int i = 31; i >= 0; i--)
        {
            int cur = min(sum[i], x/(1<

E. Tree Constructing

题意:

  • 给定\(n,d,k\),构造一颗有n个节点,直径为d,点的度数最多为k的树。

思路:

  • 直接构造一条长度为d的长链,然后在树上的点上插入叶子就行。
  • 当然也要注意插入规则,两端的点不能插入,两端靠中间挪动一个点的地方可以插入一个深度为1的子树,再往中间挪动可以插入深度为2的子树,以此类推。
  • 感觉这道构造题还是挺考验码力的。开始写的是dfs结果MLE了也不知道为啥,改成了bfs。
#include
using namespace std;
int n, d, k;
int cnt;
vector> ans;

void bfs(int depth, int fa, int deg)
{
    if(depth <= 0) return;
    if(cnt == n+1) return;
    queue> q;
    for(int i = 1; i <= deg; i++)
    {
        ans.push_back({fa, cnt});
        q.push({cnt, 1});
        cnt++;
        if(cnt == n+1) return;
    }
    depth--;//已经用了一层了
    while(q.size())
    {
        int x = q.front().first, d = q.front().second;
        if(d == depth+1) return; q.pop();
        for(int i = 1; i <= deg+1; i++)
        {
            ans.push_back({x, cnt});
            q.push({cnt, d+1});
            cnt++;
            if(cnt == n+1) return;
        }
    }
}

int main()
{
    scanf("%d%d%d", &n, &d, &k);
    cnt = 1;
    if(d >= n || (d != k && k == 1))
    {
        puts("NO");
        return 0;
    }

    //先直接构造出链条
    for(int i = 1; i <= d; i++)
        ans.push_back({cnt, cnt+1}), cnt++;
    //往枝杈上插入点
    cnt++;
    for(int i = 2, j = d; i <= j; i++, j--)
    {
        int depth = i - 1; //当前这个点能插入深度多少的叶子
        bfs(depth, i, k-2);
        if(i != j) bfs(depth, j, k-2);
        if(cnt == n+1) break;
    }
    if(cnt != n+1) puts("NO");
    else
    {
        puts("YES");
        for(auto x : ans)
            printf("%d %d\n", x.first, x.second);
    }
    return 0;
}

F. Abbreviation

题意:

  • 给几个字符串,有一定规则能够缩短这个字符串。
  • 问进行一次缩短操作后字符串最短长度为多少(算上空格)
  • 数据范围:\(n\leq 300\)

思路:

  • 我看官方题解写的。

  • \(f(i,j)\)表示以\(i,j\)开始最长能够匹配的字符串。

  • 枚举所有起点和区间长度,计算结果。
  • 注释写的比较详细了。
  • 官方题解

#include
using namespace std;
const int maxn = 300 + 10;
int f[maxn][maxn];
int n;
int len;
vector a;
int main()
{
    scanf("%d", &n);
    int len = n-1;
    for(int i = 0; i < n; i++)
    {
        string str; cin >> str;
        a.push_back(str);
        len += str.size();
    }

    for(int i = n - 1; i >= 0; i--)
        for(int j = n-1; j >= 0; j--)
        {
            if(a[i] == a[j])
                if(i + 1 < n && j + 1 < n)
                    f[i][j] = f[i+1][j+1]+1;
                else f[i][j] = 1;
        }
    int ans = len;
    for(int i = 0; i < n; i++) //枚举开头
    {
        int sum = 0;
        for(int j = 0; i + j < n; j++) //枚举长度
        {
            sum += a[i+j].size(); //长度为j时的长度和
            int cnt = 1;
            for(int pos = i+j+1; pos < n; pos++)
                if(f[i][pos] > j) //相同 则可以简化
            {
                ++cnt;  //相同的串加一
                pos += j; //跳过这个区间
            }
            //当前计算的结果 = 总长度 - 长度为j时的长度*有多少个这样的区间 + 区间数
            int cur = len - sum*cnt + cnt;
            if(cnt > 1 && ans > cur) ans = cur;
        }
    } cout << ans << endl;
    return 0;
}

你可能感兴趣的:(Codeforces Round #494(Div.3))