天天快乐编程杯中学生信奥联赛(202002) 题解

此博客原文地址:https://www.cnblogs.com/BobHuang/p/12312129.html

1.6172: Alice视察

本题目比较困难,我们将信息传递出去,而且是最短的其实只需要n-1条边,他其实就是一棵无向树。在数据结构中有一专有名词最小生成树,有两个算法,分别是kruskal(加边)及Prime(加点)。这个题目数据量比较小,不卡算法,你可以实现任一算法AC。题解所用的是Prime。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> pii;
const int N = 4*1e5+5;//边数的两倍,因为是无向图
const int M = 998244353;
#define INF INT_MAX
#define INM INT_MIN
int cnt = 1,n,m,ans = 0,num = 0;//ans为最小生成树的所有边和
int dis[5005],vis[5005],head[5005];//dis存储各节点与最小生成树的连通边的长度,其实存的就是与这个点相连的最短边,visit数组检查是否在最小生成树里了.
struct Node
{
    int to;
    int cost;
    int next;
}e[N];
inline void add(int u,int v,int w)//邻接链表存图
{
    e[cnt].to = v;
    e[cnt].cost = w;
    e[cnt].next = head[u];
    head[u] = cnt++;
}
void prim()
{
    memset(vis,0,sizeof(vis));
    priority_queue<pii,vector<pii>,greater<pii> >Q;//pii前面存的是到这个点的距离,后面是到的点的编号
    dis[1] = 0;
    Q.push(make_pair(0,1));
    while(!Q.empty() && num < n)//最小生成树的边数为n-1,!Q的作用是在图不连通的时候退出,这时不存在最小生成树
    {
        int u = Q.top().second;//取点
        int d = Q.top().first;//取边,判断是否连入生成树
        Q.pop();
        if(vis[u] != 1)//没加入生成树
        {
            vis[u] = 1;
            num++;//连入点的计数
            ans += d;//加上连入的边
            for(int i=head[u];i!=-1;i=e[i].next)//邻接链表的遍历(本来就是这样遍历的啊!!!)
            {
                if(e[i].cost < dis[e[i].to])//更新最小生成树上的距离,其实是意在连通,因为是从最小开取,所以这里满足的时候只有还没连通这个点的时候.
                {
                    dis[e[i].to] = e[i].cost;
                    Q.push(make_pair(e[i].cost,e[i].to));
                }
            }
        }
    }
}
int main()
{
    memset(head,-1,sizeof(head));//0应该也可以.毕竟我的cnt都是从1开始嘻嘻
    for(int i=0;i<5005;++i) dis[i] = INF;//不要忘记dis的初始化为无穷大,不然出不来答案,memset初始化可能会变成奇怪的数字(具体也不清楚..)
    scanf("%d %d",&n,&m);
    while(m--)
    {
        int x,y,z;
        scanf("%d %d %d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    prim();
    printf("%d\n",ans);
}

2.6178: Alice运快递

这是一个很经典的01背包问题,一个物品可以选一次,所以我们这时候的状态是从dp[i-1][j]过来的,当前物品是否可以放下,是不是更大。只能选一次第二重循环要倒着进行。
第二问是个很简单的贪心问题,我们可以选择体积最小的货物装上。

#include <bits/stdc++.h>
using namespace std;
int w[1005], c[1005], dp[100005];
int main()
{
    int m, n;
    while (cin >> m >> n)
    {
        for (int i = 0; i < n; i++)
            cin >> w[i] >> c[i];
        memset(dp, 0, sizeof(dp));
        for (int i = 0; i < n; i++)
            for (int j = m; j >= w[i]; j--)
                dp[j] = max(dp[j], dp[j - w[i]] + c[i]);
        sort(w, w + n);
        int ans = 0, sum = 0;
        for (int i = 0; i < n; i++)
        {
            if (sum + w[i] <= m)
                sum += w[i], ans++;
            else
                break;
        }
        cout << dp[m] << " " << ans << "\n";
    }
    return 0;
}

3.6173: 相同行程查询

这个题目有些困难,我们需要知道这个人的车次,之后我们会找到人然后对应到这个车次。最终我们要查询的是车次,当然我们可以进行排序之后二分。不过这个题目有个更好用的东西,叫map,是stl中的一个。map<key,value>也叫键值对,前面可以把放键,可以理解为就是一个下标,后面放值。所以就是人和车次的map以及车次和人数的map。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
map<string,string> m;
map<string,int>p;
int main()
{
    int n,a;
    string x,y;
    cin>>n;
    while(n--)
    {
        cin>>x>>a;
        while(a--)
        {
            cin>>y;
            m[y]=x;
        }
    }
    cin>>n;
    while(n--)
    {
        cin>>y;
        p[m[y]]++;
    }
    cin>>n;
    while(n--)
    {
        cin>>x;
        cout<<p[x]<<"\n";
    }
    return 0;
}

4.6177: Alice玩汉诺

这个题目我们可以采用常规做法,就是我们递归的时候可以统计移动次数。当然也可以找到递推式,是这样的,数量级大的情况就必须使用递推式了
A->B=(上一次)A->C->B
B->C=(上一次)B->A->C
c->A=(上一次)C>B->A
我们可以设置变量去统计他们

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL  n,a[8];
int main()
{
    while(~scanf("%lld",&n))
    {
        memset(a,0,sizeof(a));
        for (int i = 1; i <= n; ++i)
        {
            if (i & 1)
            {
                a[2] = a[1] + a[4] + 1;
                a[3] = a[4] + a[5];
                a[6] = a[1] + a[5];
            }
            else
            {
                a[1] = a[2] + a[6];
                a[4] = a[2] + a[3];
                a[5] = a[3] + a[6];
            }
        }
        printf("A->B: %lld\n", a[1]);
        printf("A->C: %lld\n", a[2]);
        printf("B->A: %lld\n", a[3]);
        printf("B->C: %lld\n", a[4]);
        printf("C->A: %lld\n", a[5]);
        printf("C->B: %lld\n", a[6]);
    }
    return 0;
}

5.6179: Alice排数字

其实我们可以给其中的8和2拿出来看看有多少个282,排列一定是282828282····
也就是28不断循环的,其中282的个数和2和8均有关,n个2就有n-1个282,m个8就有m个282,两者取小,当然不能是负数。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    while(cin>>s)
    {
        int a[10]={0};
        for(int i=0;s[i];i++)
            a[s[i]-'0']++;
        cout << max(0,min(a[2]-1,a[8]))<<"\n";
        cout<<"Happy Birthday!\n";
    }
    return 0;
}

6.6181: Alice与闪电

这个一个比较复杂的循环题,我们可以将其分两三输出,前m/2行,中间行,后m/2行,前m/2行前面的空格分析下,为m-i个,*为i+1个,中间行全是是n个,然后依次类推。至于中间用空行隔开,我们可以用一个旗子来表示,刚开始没有插旗子,不能输出空行,执行一次就插上了旗子,当然是否要输出空行要在插旗子之前。详见代码flag的操作。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    int flag=0;
    while (cin >> n)
    {
        if(flag)cout<<"\n";
        flag=1;
        int m = n / 2;
        for (int i = 0; i < m; i++)
        {
            for (int j = m; j > i; j--)
                cout << " ";
            for (int j = 0; j <= i; j++)
                cout << "*";
            cout << "\n";
        }
        for (int i = 0; i < n; i++)
        {
            cout << "*";
        }
        cout << "\n";
        for (int i = m - 1; i >= 0; i--)
        {
            for (int j = 0; j < m; j++)
                cout << " ";
            for (int j = 0; j <= i; j++)
                cout << "*";
            cout << "\n";
        }
    }
    return 0;
}

7.6180: Alice玩井棋

这是一个比较有趣的游戏,但是不同的思路实现起来难度可能不同,在这里推荐了一个比较简单的实现,
1.横或竖坐标均相同
2.在左上到右下的对角线上
3.在右上到左下对角线上

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int x1,x2,x3,y1,y2,y3;
    while(cin>>x1>>y1)
    {
        cin>>x2>>y2;
        cin>>x3>>y3;
        int f=0;
        if((x1==x2&&x2==x3)||(y1==y2&&y2==y3)) f=1;
        if(x1==y1&&x2==y2&&x3==y3)  f=1;
        if(x1+y1==4&&x2+y2==4&&x3+y3==4) f=1;
        if(f) cout<<"Win"<<endl;
        else cout<<"Continue"<<endl;
    }
    return 0;
}

8.6174: Alice与甜甜圈

空圆柱体的体积,体积为底面积高,
圆环面积为(R*R-r*r)
PI
注意这个题目给我们的数是实数

#include<bits/stdc++.h>
using namespace std;
int main()
{
    double R,r,h;
    double PI = acos(-1);//3.1415926536
    while(cin>>R>>r>>h)
    {
        double ans = PI*h*(R*R - r*r);
        printf("%.2f\n",ans);
    }
    return 0;
}

9.6175: Alice买口罩

10个口罩一定会浪费掉,所以你买到y个口罩,买x个一次,次数为y/x

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int x,y;
    cin>>x>>y;
    cout<<y/x;
    return 0;
}

10.6176: 武汉加油!中国加油!

简单C语言输出,可以复制粘贴,尽量减少错误

#include <bits/stdc++.h>
using namespace std;
int main()
{
    cout<<"Fighting, Wuhan! Stay strong, China!\n";
}

猜你喜欢

转载自www.cnblogs.com/BobHuang/p/12312129.html