美文网首页
2018-19赛季联合新生训练赛第三场

2018-19赛季联合新生训练赛第三场

作者: StilllFantasy | 来源:发表于2018-12-09 21:43 被阅读0次

本次训练赛误打误撞弄了个AK(因为H题数据范围小,打表...逃...),以后继续努力,看此篇文章的你也要更加努力哦。

鉴于此次训练赛大家都还做的不错,我就再来个部分题解吧(偷个懒..emm)

选几个出问题多的

问题 E:取数排列

  • 这个问题好像很多同学被绊了一下,这里看无非1~9回溯一波就可以了啊
  • 注意回溯的标记数组问题
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int v[20];
int ans[20];
int tot;
int n, m;
int ok = 1;
void dfs(int k)
{
    if (k > n)
    {
        tot++;
        if (tot == m)
            for (int i = 1; i <= n; i++)
            {
                cout << ans[i];
                ok = 0;
            }
        return;
    }
    for (int i = 1; i <= n; i++)
        if (!v[i])
        {
            ans[k] = i;
            v[i] = 1;
            dfs(k + 1);
            if (!ok)
                return;
            v[i] = 0;
        }
}
int main()
{
    cin >> n >> m;
    dfs(1);
    return 0;
}

问题 H:自然数无序拆分

  • 这个应该就是很多人离AK的分水岭,赛时的我也没想出正解的DP,就算想出,一时半会也实现不出来
  • 看一眼数据 N<=100 (虚...我又发现新大陆啦,天生爱打表..理理思路迅速码完代码,打表搜答案,O(1)复杂度解决问题....没想到罚时有些许的优势拿了Rank 1 (不能骄傲,继续加油)
  • 这个题我分阶段解决,然后把答案合并,分阶段的意思是,一个数分成若干数相加,有几种情况?答案是N中,最少是 1 个数,最多是 N个数。
  • 下边贴上我的搜索代码:
#include <cstdio>
#include <iostream>
using namespace std;
int n, k, tot;
void dfs(int now, int sum, int cur)
{
    if (cur == k)
    {
        if (sum == n)
            tot++;
        return;
    }
    for (int i = now; sum + i * (k - cur) <= n; i++)  //想一想,这里其实没必要往下搜了,相当于剪枝了
        dfs(i, sum + i, cur + 1);
}

int main()
{
    //freopen("std.in","w",stdout);
    for (int i = 1; i <= 100; i++)   //这里枚举1到100所有数
    {
        n = i;
        tot = 0;
        for (int j = 1; j <= n; j++)  //这里枚举他分成几部分
        {
            k = j;
            dfs(1, 0, 0);
        }
        cout << tot << endl;
    }
}
  • 下面贴上我提交的代码:
#include<iostream>
using namespace std;
int k[200]={1,2,3,5,7,11,15,22,30,42,56,77,101,135,176,231,297,385,490,627,792,1002,1255,1575,1958,2436,3010,3718,4565,5604,6842,8349,10143,12310,14883,17977,21637,26015,31185,37338,44583,53174,63261,75175,89134,105558,124754,147273,173525,204226,239943,281589,329931,386155,451276,526823,614154,715220,831820,966467,1121505,1300156,1505499,1741630,2012558,2323520,2679689,3087735,3554345,4087968,4697205,5392783,6185689,7089500,8118264,9289091,10619863,12132164,13848650,15796476,18004327,20506255,23338469,26543660,30167357,34262962,38887673,44108109,49995925,56634173,64112359,72533807,82010177,92669720,104651419,118114304,133230930,150198136,169229875,190569292};
int main()
{
    int n;
    cin>>n;            //输入
    cout<<k[n-1];    //输出
    return 0;
}
  • 赛后问朋哥这题正解:朋哥:DP!
  • 一个小时后...
  • 两个小时后...
  • 朋哥:再等我10分钟
  • 半小时后....
  • 第二天,代码出来了....(这里不是说朋哥敲的慢,朋哥心里还惦记着他的电路呢,明天下午考试啊!为朋哥祈祷,别挂科,哈哈)传送门DP解法

问题 K:最少移动次数

  • 每堆糖果必定变成总糖果的平均数
  • 怎么说呢,我没法证明我是对的,但是找不出反例来,就是数的传递呗,1给2,2给3,3给4..
  • 如果某堆糖果小于平均数,他就跟下一堆要,传递次数+1,如果恰好是平均数,传递次数不变,如果大于平均数,那么传递给下一堆,传递次数+1
  • Lrj蓝书有一道跟这个相似的题,不过那个题要难些,求的是最少传递的糖果数,有兴趣同学可以去翻一下 Page 4
  • 细节见代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
long long c[500];
long long tot, k, x;
long long ans;
long long num[500];
int n;
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> num[i];
        tot += num[i];
    }
    k = tot / n;
    tot = 0;
    if (num[1] < k)
    {
        tot++;
        num[2] -= (k - num[1]);
        num[1] = k;
    }
    else if (num[1] > k)
    {
        tot++;
        num[2] += (num[1] - k);
        num[1] = k;
    }
    for (int i = 2; i < n; i++)
    {
        if (num[i] == k)
            continue;
        if (num[i] < k)
        {
            tot++;
            num[i + 1] -= (k - num[i]);
            num[i] = k;
        }
        else if (num[i] > k)
        {
            tot++;
            num[i + 1] += (num[i] - k);
            num[i] = k;
        }
    }
    cout << tot;
    return 0;
}

PS 这三个题大家出问题多一些,暂时先讲这三个题,其他题若有需要讨论的可以私聊我QQ

相关文章

网友评论

      本文标题:2018-19赛季联合新生训练赛第三场

      本文链接:https://www.haomeiwen.com/subject/vjokhqtx.html