Codeforces Round #428 (Div. 2)比赛总结

这次终于涨分了,比赛的时候思路也比前几次清晰多了,继续加油。
A题题意:A每天可以获得ai个糖,他会把糖给B,但是一天顶多给8颗,问最少要几天B获得的糖数不少于k颗。
WA了几次,一开始题意理解错了。。遇到水题还是得冷静分析分析。
最直接的思路就是如果A手上的糖数多于8颗,就给B8颗,如果少于8颗,就全部给他。
直接贴代码。

#include<iostream>
using namespace std;
int main()
{
    int n, k;
    cin >> n >> k;
    int num[105];
    for (int i = 1;i <= n;i++)
    {
        cin >> num[i];
    }
    int cnt = 0;
    int temp = 0;
    int ans = 0;
    while (temp<k&&ans<n)
    {
        if (cnt + num[++ans] > 8)
        {
            temp += 8;
            cnt = cnt + num[ans] - 8;
        }
        else
        {
            temp += cnt + num[ans];
            cnt = 0;
        }
    }
    if (temp < k)cout << -1 << endl;
    else cout << ans << endl;
    return 0;
}

B题题意:有k种部队,每个部队有ai个人,他们要坐飞机,飞机座位有n行,每行8个座位,1和2,3和4,4和5,5和6,7和8连在一起。不同部队的人不能坐在连在一起的位置,请问能不能有没有方案安排他们坐好。

一开始以为是大力if,但是看完claris精短的代码后,顿时豁然开朗。。
思路:相邻的座位其实分成3种,1个位置,2个位置,和4个位置。我们只要判断是否能把一个部队的人塞进这几种位置中的一个,如果都不行,那就NO,如果可以,该位置减一。
另外,如果可能的话,最好把一个部队的放进4个位置当中,这样影响的座位最少,可以有更多的不同的部队坐下去。
感觉像脑筋急转弯。。
代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

#pragma warning (disable:4996)
const int maxn = 10005;
int a[maxn];
int c[5];
int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    int x;
    memset(c, 0, sizeof(c));
    for (int i = 1;i <= k;i++)
    {
        scanf("%d", &x);
        a[x]++;
    }
    c[2] = n * 2;
    c[4] = n;
    int i,j;
    for ( i = 10000;i > 0;i--)
    {
        while (a[i]--)
        {
            for ( j = 4;j;j--)if(c[j])
            {
                c[j]--;
                int t = min(i, j);
                a[i - t]++;
                if (j - i - 1 > 0)c[j - i - 1]++;
                break;
            }
            if (!j)
            {
                puts("NO");
                return 0;
            }
        }
    }
    puts("YES");
    return 0;
}

C题题意:求一颗树从结点1上到每个叶子深度的期望。
WA了几次,原因没有仔细思考到每个叶子的概率,以为到每个叶子的概率是一样的,然而不是。
思路很简单,深搜广搜都可以,维护每个结点的深度和到该结点的概率即可。

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
#pragma warning (disable:4996)
const int maxn = 100005;
vector<int>V[maxn];
int state[maxn];
bool vis[maxn];
double q[maxn];
queue<int>Q;
void bfs()
{
    Q.push(1);
    vis[1] = 1;
    state[1] = 0;
    q[1] = 1;
    double ans = 0;
    //int cnt = 0;
    //int length = 0;
    while (!Q.empty())
    {
        int u = Q.front();Q.pop();
        int Size = V[u].size();
        if (u != 1 && Size == 1)
        {
            ans += q[u] * state[u];
        }
        else
        {
            int v;
            int Count;
            if (u == 1)Count = Size;
            else Count = Size - 1;
            for (int i = 0;i <Size; i++)if (vis[V[u][i]] == 0)
            {
                v = V[u][i];
                vis[v] = 1;
                state[v] = state[u] + 1;
                q[v] = q[u] / (double)(Count);
                Q.push(v);
            }
        }
    }
    printf("%.15f\n", ans);
}
int main()
{
    int n;
    scanf("%d", &n);
    if (n == 1) { printf(" 0.000000000000000\n");return 0; }
    int u, v;
    for (int i = 0;i < n - 1;i++)
    {
        scanf("%d%d", &u, &v);
        V[u].push_back(v);
        V[v].push_back(u);
    }
    bfs();
    return 0;
}

D题题意:给定一个序列,要求求出该序列所有gcd不为1的序列(不要求连续),并定义该序列的值为序列元素个数*gcd(该序列的所有元素),求序列值的总和。
思路:首先我们可以求出以i为公约数的序列长度总和。再减去其中最大公约数不为i的序列长度。比如2 4 6 ,以2为公约数序列有2,4,6,2 4,2 6,4 6,2 4 6,但是其中4,6,两个序列的最大公约数并不为2(为其本身),所以要减掉这两个。因为小的包括大的,所以应该从后往前推。具体实现见代码。
代码:

#include<iostream>
#include<cstring>
using namespace std;
#pragma warning (disable:4996)
const int maxn = 1000005;
const int mod = 1000000007;
int a[maxn];
int t[maxn];
int f[maxn];
void init()
{
    t[0] = 1;
    for (int i = 1;i <= 1000000;i++)
        t[i] = t[i - 1] * 2 % mod;
}
int main()
{
    init();
    int n;
    cin >> n;
    int x;
    memset(a, 0, sizeof(a));
    for (int i = 1;i <= n;i++)
    {
        scanf("%d", &x);
        a[x]++;
    }

    int ans = 0;

    for (int i = 1000000;i > 1;i--)
    {
        x = 0;
        for (int j = i;j <= 1000000;j += i)x += a[j];
        if (x)
        {
            f[i] = (1LL*x*t[x - 1]) % mod;
            for (int j = i + i;j <= 1000000;j += i)
                f[i] = (1LL*f[i] - f[j]+mod) % mod;
            ans = (ans + 1LL * f[i] * i) % mod;
        }
    }
    printf("%d\n", ans);
    return 0;

}

这里有几个问题:1,为什么要预处理一个2的幂?2,为什么f[i]可以这么算?留给读者思考。

E题题意:一个墙壁连着两个城堡,总共有k油漆可以涂在城堡上,墙壁的硬度等于连着的两个城堡涂得油漆量相乘,现在给出城堡之间的连通关系,问如何分配油漆使得总硬度最大。

这道题的思路不太好说,直接说结论,结论就是求出这个图的最大团,然后将k油漆均衡分配给最大团中的城堡,这样会使得总硬度最大。

我想给出严谨的证明,但我的潜意识觉得不需要,因为我们只是在做题而已,而题内部的玄妙其实不用管太多,只需要找到其中包含的规律即可。

话不多说,开始找规律。

如果只有两个城堡相连,那么硬度值为k^2/4, 那么如果有3个城堡相连呢,我们学过均值不等式,显然硬度值取最大值时,k是均匀分配给这三个城堡的,这时候硬度值为k^2/3。 这个值是比前者大的,如果不止三个城堡呢,在前者(三个城堡相连)的基础上再加上2个相连的城堡,我们可以试试分配看,可以发现,不管怎么分配,都会比k^2/3小,这是我们可以推广到很多城堡:倘若有n个城堡相互相连(专业术语称为最大团),那么我们可以将k均衡分配给这n个城堡,得出来的硬度值为:(n-1)/2n *k^2,必然是最大的,其他的城堡分配的油漆则为0。

这样巧妙的思考完后,那么问题来了,如何求一个图上的最大团呢,实际上这是一个NP问题,还没有很好的方式来解决这个问题,但基于这个题的数据量(点的个数最多为40),我们其实只需要爆搜(笑)即可。

一个简单的DFS即可。

代码如下:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#pragma warning (disable:4996)
const int maxn = 45;
int a[maxn][maxn];
vector<int>V;
int ans = -1;
int n, k;
bool vis[maxn];

bool check(int idx)
{
    int Size = V.size();
    for (int i = 0;i < Size;i++)
        if (a[idx][V[i]] == 0)
            return 0;
    return 1;
}

void dfs(int idx)
{
    if (idx > n)
        return;
    if (ans >=(int) (V.size() + n - idx ))
        return;
    bool flag = false;
    for(int i=idx+1;i<=n;i++)
        if (check(i))
        {
            flag = true;
            V.push_back(i);
            //vis[i] = 1;
            dfs(i);
            //vis[i] = 0;
            V.pop_back();
        }
    if (!flag)
    {
        int Size = V.size();
        ans = max(ans, Size);
    }
}
int main()
{

    cin >> n >> k;
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= n;j++)
            scanf("%d", &a[i][j]);
    dfs(0);
    if (ans == -1)
    {
        printf("%.12f\n", (double)k *k / 4.0);
    }
    else
        printf("%.12f\n", (double)k*k * (ans - 1) / (double)(2 * ans));
    return 0;
}

信心的读者可以发现,dfs里面有个巧妙的剪枝,.
if (ans >=(int) (V.size() + n - idx ))return
为什么可以这样剪呢,留给读者思考~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值