重庆师范大学第一届ACM选拔赛

下面的题解不会给出题目所以在开头放出比赛链接传送门

本场比赛大部分都是模板题,但是任有记录的必要性

重庆师范大学第一届ACM选拔赛

    • 不一样的食物链
    • 有趣的求和
    • 统计患病人数
    • 皮皮想拜师
    • 爱玩游戏的Tom
    • 天选子
    • 团日活动
    • 标准签到题
    • 炎炎消防队

不一样的食物链

题目分析:题目说的是每一个动物都有天敌,所以对于每组数据我们只需要标记一下猎物,然后再遍历一下所有出现过的动物,如果在遍历的过程中发现有一个动物没有被标记说明他没有天敌,也就不符合题目的意思,直接输出0并返回,反之输出1,这个题目需要注意的就是输入的是字符串,所以需要仔细看题目
set做法和杭电那道产生冠军题目基本上是一个意思
set
开两个set,一个用于去存储所有出现过的动物,另一个存储猎物,并在最后判断一下,如果两个set的大小一样,也就是说所有动物的数量和有天敌的数量相等那么也就意味着,所有动物都有天敌,形成了一个环~

#include 
using namespace std;
int main()
{
     
    set<string> a, b;
    string c, d;
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
    {
     
        cin >> c >> d;
        a.insert(c);
        a.insert(d);
        b.insert(d);
    }
    if (a.size() == b.size())
        puts("1");
    else
        puts("0");
}

map
同样是开两个map,一个用于存储,一个用于迭代器遍历去寻找没有天敌的动物,一旦找到输出0并返回,否则输出1~

#include 
using namespace std;

map<string, int> ma;
map<string, int>::iterator it; //迭代器

int main()
{
     
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
    {
     
        string s1, s2;
        cin >> s1 >> s2;
        ma[s1];
        ma[s2]++; //把有天敌的标记一下
    }
    for (it = ma.begin(); it != ma.end(); ++it)
    {
     
        if (it->second == 0)
        {
     
            putchar('0');
            return 0;
        }
    }
    putchar('1');
}

有趣的求和

dfs

暂且一搁,还不是很懂~
AC代码

#include 
using namespace std;
int n, a[25];
char b[25];
vector<string> ans;
void dfs(int dep, int val)
{
     
    if (dep == n)
    {
     
        if (a[dep] != val)
            return;
        string s;
        for (int i = 2; i <= n - 1; i++)
            s += b[i];
        ans.push_back(s);
        return;
    }
    b[dep] = '-';
    dfs(dep + 1, val - a[dep]);
    b[dep] = '+';
    dfs(dep + 1, val + a[dep]);
}
int main()
{
     
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    dfs(2, a[1]);
    printf("%d\n", ans.size());
    for (int i = 0; i < ans.size(); ++i)
        cout << ans[i] << endl;
    return 0;
}

统计患病人数

并查集模板题

题目大意:说是有一个人患病,然后这个病会传染,像极了今年的新冠疫情,一个人可以在多个组织,只要这个组织的人中有一个患病那么所有人都会患病,最后要升序输出所有的人员编号
AC代码

#include 
using namespace std;
const int maxn = 1e5 + 10;
int pre[maxn], a[maxn];
int find(int x)
{
     
    if (pre[x] != x)
        pre[x] = find(pre[x]);
    return pre[x];
}
void join(int x, int y)
{
     
    int a = find(x);
    int b = find(y);
    if (a != b)
        pre[a] = b;
}
int main()
{
     
    int x, n, m, num, xx, y, cnt;
    for (int i = 0; i <= maxn; ++i)
        pre[i] = i;
    while (~scanf("%d%d%d", &x, &n, &m))
    {
     
        cnt = 0;
        for (int i = 1; i <= m; ++i)
        {
     
            scanf("%d%d", &num, &xx); //让第一个人成为父节点
            for (int i = 1; i < num; ++i)
            {
     
                scanf("%d", &y);
                join(xx, y);
            }
        }
        for (int i = 0; i <= x; ++i)
            if (find(n) == find(i))
                a[++cnt] = i;
        printf("%d", cnt);
        for (int i = 1; i <= cnt; ++i)
            printf(" %d", a[i]);
        puts("");
    }
    return 0;
}

皮皮想拜师

简单bfs

一看到这个题目我就想到了poj一道类似的题目,所以这也是一个简单的bfs模板题,只要分三种情况分别进入队列就行,然后只要找到了对应的数字就直接返回就行
AC代码

#include 
using namespace std;
const int maxn = 1e5 + 10;
int m, n;
int vis[maxn];
struct node
{
     
    int x, step;
};

int judge(int x)
{
     
    if (x >= 0 && x < maxn && !vis[x])
        return 1;
    return 0;
}
int bfs(int x)
{
     
    queue<node> q;
    node s, e;
    s.x = x;
    s.step = 0;
    q.push(s);
    while (!q.empty())
    {
     
        s = q.front();
        q.pop();
        if (s.x == m)
            return s.step;
        e.x = s.x * 2;
        if (judge(e.x))
        {
     
            e.step = s.step + 1;
            vis[e.x] = 1;
            q.push(e);
        }
        e.x = s.x + 1;
        if (judge(e.x))
        {
     
            e.step = s.step + 1;
            vis[e.x] = 1;
            q.push(e);
        }
        e.x = s.x - 1;
        if (judge(e.x))
        {
     
            e.step = s.step + 1;
            vis[e.x] = 1;
            q.push(e);
        }
    }
    return -1;
}
int main()
{
     
    scanf("%d%d", &m, &n);
    printf("%d\n", bfs(n));
}

爱玩游戏的Tom

01背包

题目分析:刚刚开始,我一直以为是一个贪心,结果贪来贪去的发现自己总是过不了题,后面经由大佬点播,原来这是一个经典的01背包,知道每个物品的消耗和价值,然后求在一个固定容器里面求得价值最大,很明显是一个01背包的题目,确实,很久很久没做dp的题目了,dp是一种思维,长时间不做确实很难跟上,但是这个01背包的模板题把我给困住了,说明还是没有深刻理解dp的思想。

滚动数组
由于这个题目的范围比较大,所以建议使用滚动数组减少空间的消耗

#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int dp[maxn];
struct node
{
     
    int wi, vi;
} w[maxn];
int main()
{
     
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d%d", &w[i].wi, &w[i].vi);
    for (int i = 1; i <= n; ++i)
        for (int j = m; j >= w[i].wi; --j)
            dp[j] = max(dp[j], dp[j - w[i].wi] + w[i].vi);
    printf("%d\n", dp[m]);
}

如果喜欢压码的同学,可以参考下面的代码,其实也差不多
滚动数组2

#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int dp[maxn];
struct node
{
     
    int wi, vi;
} w[maxn];
int main()
{
     
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
    {
     
        scanf("%d%d", &w[i].wi, &w[i].vi);
        for (int j = m; j >= w[i].wi; --j)
            dp[j] = max(dp[j], dp[j - w[i].wi] + w[i].vi);
    }
    printf("%d\n", dp[m]);
}

01背包二维数组

#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int dp[1010][maxn];
struct node
{
     
    int wi, vi;
} w[maxn];
int main()
{
     
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d%d", &w[i].wi, &w[i].vi);
    for (int i = 1; i <= n; ++i)
        for (int j = 0; j <= m; ++j)
            if (j >= w[i].wi)
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i].wi] + w[i].vi);
            else
                dp[i][j] = dp[i - 1][j];
    printf("%d\n", dp[n][m]);
}

天选子

PS:刚刚开始看到题目的时候,一种油然而生的熟悉感,这不就是一个list的模板题吗,与杭电的一道题目十分相像
方法一:数组模拟
我们用数组去模拟一遍,把每次出队列的同学用vis标记一次,代表该同学已经出了队列,周而复始,知道数组里面的元素小于三个为止

#include 
using namespace std;
const int maxn = 1e5 + 10;
int vis[maxn];
vector<int> q;

int main()
{
     
    int n, m;
    scanf("%d%d", &n, &m);
    int sum = n, now = 2, num = 0;
    while (sum > 3)
    {
     
        for (int i = 1; i <= n; ++i)
        {
     
            if (!vis[i]) //找出没有被标记的同学
            {
     
                num++;          //记录数量
                if (num == now) //如果数量等于当前需要出队列的值
                {
     
                    vis[i] = 1; //标记改同学已经出队列
                    sum--;      //人数减少一个
                    num = 0;    //继续往后遍历
                }
            }
        }
        num = 0;
        now = 5 - now; //3,2反复反复遍历
    }
    int ans = 0;
    for (int i = 1; i <= n; ++i)
    {
     
        if (!vis[i])
        {
     
            ans += i;
            q.push_back(i); //放入数组
        }
    }
    if (ans > m)
    {
     
        for (int i = 0; i < q.size(); ++i)
            if (i == 0)
                printf("%d", q[i]);
            else
                printf(" %d", q[i]);
    }
    else
        printf("%d\n", abs(ans - m));
}

方法二:STL之List
PS:一般来说这个题目是用来锻炼list入门的题目,所以题目并不是很难,但是需要注意的细节很多。

#include 
using namespace std;
int main()
{
     
    int n, m, k = 2;
    scanf("%d%d", &n, &m);
    list<int> a;
    list<int>::iterator it; //迭代器储存的是元素的地址
    for (int i = 1; i <= n; i++)
        a.push_back(i);
    while (a.size() > 3)
    {
     
        int num = 1;
        for (it = a.begin(); it != a.end();)
        {
     
            if (num++ % k == 0)
                it = a.erase(it);
            else
                it++;
        }
        k = 5 - k;
    }
    int ans = 0;
    for (it = a.begin(); it != a.end(); it++)
        ans += *it; //加了*号代表该地址的值
    if (ans > m)
    {
     
        for (it = a.begin(); it != a.end(); it++)
        {
     
            if (it != a.begin())
                putchar(' ');
            printf("%d", *it);
        }
    }
    else
        printf("%d\n", abs(ans - m));
    return 0;
}

团日活动

PS:思维题,不是很懂,先附上代码吧

#include 
using namespace std;
typedef long long ll;
int main()
{
     
    ll t, n, m;
    scanf("%lld", &t);
    while (t--)
    {
     
        ll ans = 0;
        scanf("%lld%lld", &n, &m);
        if (m & 1)
        {
     
            ans = (m + 3) * (m - 1) / 2 / 2;
            ll s = n - m + 1;
            ans += (s + 1) / 2;
        }
        else
        {
     
            ans = (m + 2) * m / 2 / 2;
            ll s = n - m;
            ans += (s + 1) / 2;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

标准签到题

简单签到题

#include 
using namespace std;
const int maxn = 1e5 + 10;
char c[maxn];
int main()
{
     
    int ans;
    gets(c);
    ans = 0;
    int lc = strlen(c);
    for (int i = 0; i < lc; ++i)
    {
     
        if (c[i] == 'o' && c[i + 1] == 'r' && c[i + 2] == 'a')
            ++ans, i += 2;
    }
    if (ans)
        printf("%d\n", ans);
    else
        puts("yare yare daze");
}

炎炎消防队

模拟退火模板题
怎么说呢,我也没搞懂模拟退火怎么模拟,也没去搞懂,感觉也是一个活生生的模板题,所以就没花什么时间去处理,主要也是感觉这一类题目很冷门,希望不会是我的错觉吧

#include 
using namespace std;
const double eps = 1e-8;//终止温度
double y;
double func(double x)//计算函数值
{
     
    return 7 * pow(x, 7.0) + 6 * pow(x, 6.0) + 2 * pow(x, 3.0) + 8 * pow(x, 2.0) - y * x;
}
double solve()
{
     
    double T = 100;//初始温度
    double delta = 0.98, x = 50.0;//降温系数,x的初始值
    double now = func(x);//计算初始函数值
    double ans = now;//返回值
    while (T > eps)//eps是终止温度
    {
     
        int f[2] = {
     1, -1};
        double newx = x + f[rand() % 2] * T;
        if (newx >= 0 && newx <= 100)
        {
     
            double next = func(newx);
            ans = min(ans, next);
            if (now - next > eps)//更新x
            {
     
                x = newx;
                now = next;
            }
        }
        T *= delta;
    }
    return ans;
}
int main()
{
     
    int cas;
    scanf("%d", &cas);
    while (cas--)
    {
     
        scanf("%lf", &y);
        printf("%.4f\n", solve());
    }
}

今天是七夕,所以文案也应当是甜甜的~

山水一程,三生有幸,,,白茶清欢也无事,我在等风也等你~

你可能感兴趣的:(重庆师范大学第一届ACM选拔赛)