第七届山东省省赛题解

Posted aiguona

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第七届山东省省赛题解相关的知识,希望对你有一定的参考价值。

A - Julyed

类型:

水题

题意:

Julyed正在为她的大学英语六级考试做准备。她有N个字要记,但只剩M天了。如果她记不住这些话,她就不会通过大学英语六级考试。如果她不能通过大学英语六级考试,她就会不高兴。但如果她在某一天记得太多的话,她也会不开心。如果她不高兴,汤姆就会不高兴。所以她会在一天之内记住尽可能少的单词。为了快乐和快乐,在一天中,最多将会有多少个单词会被记住呢?

题解:

水题 N / 天数  上取整

代码:

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int a,b;
        cin >> a >> b;
        int ans = ceil(a * 1.0/ b * 1.0);
        cout << ans << endl;
    }
    return 0;
}

B - Fibonacci

题意:

给定一个整数N,判断N是否可以写成不连续的斐波那契数之和。若能输不能输出-1,若能输出 N = f1 + f2 + f3 + … + fn.

题解:

首先不存在-1的情况。齐肯多夫定理:任何正整数都可以唯一地表示成若干个不连续的斐波那契数之和。

证明:

数学归纳法,可以自行查找

那么只需要打好斐波那契表,逆序遍历Fib数,存到输出数组里,最后按照格式逆序输出即可

int Fib[50];
int ans[50];
void init()
{
    Fib[1]=1,Fib[2]=2;
    for(int i=3;i<=43;i++)
        Fib[i]=Fib[i-1]+Fib[i-2];
}
int main()
{
    init();
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        int len=0,put=n;
        for(int i=43;i>=1;i--)
            if(n>=Fib[i])
                ans[++len]=Fib[i],n-=Fib[i];
        cout<<put<<"="<<ans[len];
        for(int i=len-1;i>=1;i--)
            cout<<"+"<<ans[i];
        cout<<endl;
    }
}

C - Proxy

题意:

寻找给定起点和目标点的最短路上,与起点相连的编号最小的节点序号。类型:图论,思维

题解:

这是一道最短路问题。 要求有以下几点:

①求最短路,如果没有输出-1;
②输出最短路当中的距离起点最近的那个点
③存在多个最短路时,输出最小的那个点

第一点套模版即可。
第二点,我们记录路径往往最容易查询距离终点最近的那个点。所以我们可以建反向边,这样就可以查询距离起点最近得了。
第三点,这是这道题的核心考点。 如果某点加上当前的路径长度刚好等于最短路的长度,那么说明存在两条最短路了,这时候比较当前点和原本终点的前一个点的大小,选择小的替换

题型:

图论

代码:

int INF = 1<<29;
int mp[1010][1010];
int n,m;
int d[1010];
int path[1010];
bool vis[1010];
void init(int n)
{
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
        {
            if(i==j)
                mp[i][j]=0;
            else
                mp[i][j]=INF;
        }
}

void dijkstra()
{
    int i,j,minn,v;


    for(i=0; i<n; i++)
    {
        vis[i]=0;
        d[i]=mp[n-1][i];
        if(mp[n-1][i]<INF)
            path[i]=0;
        else
            path[i]=-1;
    }
    path[n-1]=0;
    for(i=0; i<n; i++)
    {
        minn=INF;
        for(j=0; j<n; j++)
            if(!vis[j] && d[j]<minn)
            {
                v=j;
                minn=d[j];
            }
        vis[v]=1;
        for(j=0; j<n; j++)
            if(!vis[j] && d[j]>mp[v][j]+d[v])
            {
                d[j]=mp[v][j]+d[v];
                path[j]=v;
            }
            else
            {
                if(!vis[j]&&d[j]==mp[v][j]+d[v])
                {
                    path[j]=min(path[j],v);
                }
            }
    }
}
int main()
{
    int t,a,b,c;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        n+=2;
        init(n);
        for(int i=0; i<m; i++)
        {
            cin>>a>>b>>c;
            mp[b][a]=c;
        }
        dijkstra();
        if(d[0]==INF)
        {
            cout<<"-1"<<endl;
        }
        else
        {
            cout<<path[0]<<endl;
        }
    }
}

 

D - Swiss-system tournament

题意:

给出2*n个人,每次第1个人和第2个人比赛,第3个人和第4个人比赛…进行r轮比赛,每轮比完赛都进行排名,求出最后第q名是谁。给出每个人的积分 s[i],每个人的能力a[i],能力大的获胜,获胜的加1分,输了的不加分。

类型:

思维 ,归并思想

题解:

这个题目用到了归并排序的思想,每轮比赛,把赢者放一组,输者放一组,这样单个数组也是有序的,然后进行合并。时间复杂度为0(2n*R)

代码:

#include <stdio.h>
#include <algorithm>
using namespace std;
struct node
{
    int a,b;
    int index;
};
node ans[200005];
node temp1[200005];
node temp2[200005];

bool cmp(node a,node b)
{
    if(a.a==b.a)
        return a.index<b.index;
    return a.a>b.a;
}
int main()
{
    int T;
    int n,r,q;
    int i,j,k,l;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d %d",&n,&r,&q);
        for(i=0; i<2*n; i++)
        {
            scanf("%d",&ans[i].a);
            ans[i].index=i+1;
        }

        for(i=0; i<2*n; i++)
            scanf("%d",&ans[i].b);
        sort(ans,ans+(2*n),cmp);
        for(i=0; i<r; i++)
        {
            int cnt1=0,cnt2=0;
            for(j=0; j<2*n; j+=2)
            {
                if(ans[j].b > ans[j+1].b)
                {
                    ans[j].a++;
                    temp1[cnt1++] = ans[j];
                    temp2[cnt2++] = ans[j+1];
                }
                else
                {
                    ans[j+1].a++;
                    temp1[cnt1++] = ans[j+1];
                    temp2[cnt2++] = ans[j];
                }
            }
            j=0;
            k=0;
            l=0;
            while(j<cnt1 && k<cnt2)
            {
                if(temp1[j].a == temp2[k].a)
                {
                    if(temp1[j].index < temp2[k].index)
                    {
                        ans[l++] = temp1[j];
                        j++;
                    }
                    else
                    {
                        ans[l++] = temp2[k];
                        k++;
                    }
                }
                else if(temp1[j].a > temp2[k].a)
                {
                    ans[l++] = temp1[j];
                    j++;
                }
                else
                {
                    ans[l++] = temp2[k];
                    k++;
                }
            }
            while(j<cnt1)
                ans[l++] = temp1[j++];
            while(k<cnt2)
                ans[l++] = temp2[k++];
        }
        printf("%d
",ans[q-1].index);
    }

    return 0;
}

  

E - The Binding of Isaac

题意:

就是找有多少个位置满足只与‘#’有且仅有一条公共边

题解:

暴力,数据量100 坑点 就是所给区域的外围的区域也算,翻译的时候会看到。

类型:

搜索,不过数据量比较水,是个签到,不过背景很新。

代码:

 

#include<stdio.h>
int main()
{
    char map[105][105];
    int i,j,n,m,l,sum,k;
    scanf("%d",&l);
    while(l--)
    {
        k=0;
        sum=0;
        scanf("%d%d",&m,&n);
        getchar();
        for(i=0;i<105;i++)
            for(j=0;j<105;j++)
                map[i][j]=0;
        for(i=1;i<=m;i++)
        {
            for(j=1;j<=n;j++)
                scanf("%c",&map[i][j]);
            getchar();
        }
        for(i=1;i<=m;i++)
            for(j=1;j<=n;j++)
        {
            if(map[i][j]==#)
            {
                if(map[i+1][j]==0)
                    sum++;
                if(map[i-1][j]==0)
                    sum++;
                if(map[i][j+1]==0)
                    sum++;
                if(map[i][j-1]==0)
                    sum++;
            }
            if(map[i][j]==.)
            {
                if(map[i+1][j]==#)
                    k++;
                if(map[i-1][j]==#)
                    k++;
                if(map[i][j+1]==#)
                    k++;
                if(map[i][j-1]==#)
                    k++;
                if(k==1)
                    sum++;
                k=0;
            }
        }
        printf("%d
",sum);
    }
    return 0;
}

F - Feed the monkey

真~不会

G - Triple Nim

题意:

有一堆石子,一共有n个,把n个石子分成三堆,求有多少种分配的方式能够使得bob win?

题解:

算是两个题解吧。
第一:非正式的,就是打表找规律。
规律如下:
如果 n 是 奇数,直接输出 0.
如果是偶数,并且是 2 的某个次方,输出 0.
否则统计二进制中 1 的个数。
两个 1 答案为 1
三个 1 答案为 4
四个 1 答案为 13
五个 1 答案为 40
六个 1 答案为 121
可以把答案单独开个数组 F[ N ] = F [ N -1 ] * 3 +1;

第二:正式的,这就是个Nim博弈
尼姆博弈是利用二进制的思想,那么本题也可以利用二进制的思想,可知,如果要使得Alice输并且Alice为先手,只需要使得三堆石子异或等于0 即可,首先共有n个石子,把n转化成二进制来表示,假设其中有k个1存在,如果要使得三堆石子异或为0,则在三堆石子数的二进制数的每位上1的个数都要是偶数位,又可知,在二进制中,高位的1是由低位进位而得到的,也就是说高位的1可以分解成两个低位的1,当n是奇数的时候,最低位为1且没有办法分解,所以输出0,所以当n为偶数的时候,就有(3^k - 3)/6个,减去3是去掉一个为0的情况,除6是应为本题求得是排列。

题型:

博弈

代码1:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long int f[50];
int main()
{
    int T;
    cin>>T;
    f[1]=0;
    f[2]=1;
    for(int i=3;i<=49;i++)
        f[i]=f[i-1]*3+1;
    while(T--)
    {
        long long int n;
        cin>>n;
        if(n%2)
        {
            cout<<0<<endl;
        }
        else
        {
            long long int x=n;
            int num=0;
            while(x)
            {
                if(x%2)
                    num++;
                x/=2;
            }

            cout<<f[num]<<endl;
        }
    }
}

 

代码2:

#include <bits/stdc++.h>  
  
using namespace std;  
  
int main()  
{  
    int t;  
    scanf("%d",&t);  
    while(t--)  
    {  
        long long n;  
        cin>>n;  
        if(n%2)cout<<"0"<<endl;  
        else  
        {  
            int num=0;  
            while(n)  
            {  
                if(n%2)num++;  
                n=n/2;  
            }  
            long long ans=(pow(3,num)-3)/6;  
            cout<<ans<<endl;  
        }  
    }  
    return 0;  
}  

H - Memory Leak

题意:

内存泄漏是C/ c++中一个众所周知的bug。当一个字符串比预期的长时,它将访问下一个数组的内存,这将导致问题并泄漏一些信息。你可以看到一个简单的例子:
技术分享图片

 

如我们所见,如果输入字符串的长度等于或大于数组的极限长度,则额外的部分将不会被存储,下一个数组的信息将在输出时被泄漏。输出将停止,直到找到“”字符(字符串末尾的符号)。在这个问题中,程序永远不会有意外的结束,最后一个数组不会泄漏其他信息。
提供的源代码如下:
技术分享图片

INPUT
多个测试用例,第一行是整数T (T <= 20),表示测试用例的数量。
每个测试用例的第一行包含一个非空字符串,即字符串的定义,格式化为“char s1[s1_len], s2[s2_len]…”。“char”是数组的类型,它永远不会改变。s1,s2……是数组的名称。s1_len s2_len…长度限制。如果没有出错,数组应该能够存储输入字符串和“”。不同数组的定义将用逗号和空格分开。长度限制为正,长度限制之和小于10000。
然后,将会有几行字符串,其中包含两到三个部分。
第一部分是“get”或“cout”,第二部分是字符串s,表示数组的名称。s将只包含小写字母和数字数字,并以字母开头。在一个例子中s是不同的。如果第一部分是“get”,那么将会有第三部分,一个字符串,该字符串应该被输入到数组s中,输入字符串的长度将小于1000,并且只包含可见的ASCII字符。“get”操作将重写数组,无论之前数组中是什么,然后在字符串后面添加“”。不同的部分被一个空间隔开。
Case以“return 0”结尾;
整个输入文件小于10MB。
对于每个“cout”,您应该输出一行包含实际输出的内容,这意味着您应该考虑内存泄漏的问题。如果请求的数组为空,则应该输出空行。

类型:

大模拟题 ,字符串处理。还有对英语也是一种考验,很容易漏条件。

题解:

判每个数组的类型和长度,然后就检查有没有内存泄漏。

代码:

#include <bits/stdc++.h>
using namespace std;
struct node
{
    int l,r;
}e[11111];
int top;
char s[11111];
char c[11111][1111];
char op[11111];
int main()
{
    int T;

    scanf("%d",&T);

    while(T--)
    {
        memset(s,0,sizeof(s));

        int pos = 0;

        top = 0;

        scanf("%s",op);

        while(1)
        {
            scanf("%s",op);

            int len = strlen(op);

            int num = 0,i = 0;

            for(i = 0;i < len; i++)
            {
                if(op[i] != [)
                    c[top][i] = op[i];
                else break;
            }

            c[top][i] = ;

            for(i++ ;i < len; i++)
            {
                if(op[i] == ]) break;
                num = num * 10 + op[i] -0;
            }

            e[top].l = pos;

            e[top].r = pos+num;

            pos+=num;

            top++;

            char ss = getchar();

            if(ss == 
) break;
        }
        while(1)
        {
            scanf("%s",op);

            if(op[0] == r)
            {
                scanf("%s",op);

                break;
            }

            if(op[0] == c) 
            {
                scanf("%s", op);

                for(int i = 0;i < top; i++)
                {
                    if(strcmp(op,c[i]) == 0)
                    {
                        for(int j = e[i].l ; j < pos; j++)
                        {
                            if(s[j] == ) break;

                            printf("%c",s[j]);
                        }

                        printf("
");

                        break;
                    }
                }
            }
            else if(op[0] == g)
            {
                scanf("%s",op);

                for(int i = 0 ;i < top; i++)
                {
                    if(strcmp(op,c[i]) == 0)
                    {
                        gets(op);

                        int len = strlen(op);

                        int k = 1,j;

                        for( j = e[i].l ; j < e[i].r && k < len; j++, k++)
                        {
                            s[j] = op[k];
                        }

                        if(j < e[i].r)
                        {
                            s[j] = ;
                        }

                        break;
                    }
                }
            }
        }
    }

    return 0;
}

 

I - Rock Paper Scissors

真~不会

J - Execution of Paladin

题意:

先来说一下每个卡牌是什么意思,左上角写着10的那个牌叫“亡者归来”,作用是召唤7个死亡的鱼人,
下面的四个鱼人从左到右分别是:
寒光智者(攻2):作用是每个玩家抽两张牌。对这个题来说没什么用。
鱼人领军(攻3);所有的其他鱼人的攻击力+2
蓝鳃战士(攻2);冲锋(就是一上来就能打,其他的需要休息一回合。)
老瞎眼(攻2):冲锋,并且场上每有一个其他鱼人,他的攻击力就+1.

题解:

算斩杀,就是看看这一回合能造成多少伤害,
先算一共的鱼人个数,再算领军的数量,在分别算蓝鳃和老瞎眼的个数,因为这两种可以上来就打,
伤害量 = 2 * 领军 + 蓝鳃 * 2 + 2 *领军 + (鱼人个数+2)* 老瞎眼。

类型:

模拟

代码:

#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
    int T;
    cin >> T;
    char a[50];
    int o,b,c,m;
    while(T--)
    {
        int n,e;
        o = 0,b = 0,c = 0,m = 0;
        cin >> n >> e;
        getchar();
        for(int i = 1; i <= n; ++i)
        {
            gets(a);
            if(a[0] == O)
                o++;
            if(a[0] == C)
                c++;
            if(a[0] == M)
                m++;
            if(a[0] == B)
                b++;
        }
        int sha = o * (2 + 2 * m + n - 1) + b * (2 + 2 * m);
        if(sha >= e)
        {
            cout << "Mrghllghghllghg!" << endl;
        }
        else
        {
            cout << "Tell you a joke, the execution of Paladin." << endl;
        }
    }
    return 0;
}

K - Reversed Words

题意:

字符串反转

题解:

就是将每个单词进行转置。

类型:

字符串操作

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int M = 1e6;
char a[M];
char b[M];
int main()
{
    int T;
    cin >> T;
    getchar();
    while(T--)
    {
        char c;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(int i = 0;;++i)
        {
            scanf("%c",&a[i]);
            if(a[i] == 32)
            {
                for(int j = i - 1; j >= 0;--j)
                {
                    printf("%c",a[j]);
                }
                cout <<  ;
                i = -1;
            }
            if(a[i] == 
)
            {
                for(int j = i - 1; j >= 0; --j)
                {
                    printf("%c",a[j]);
                }
                cout << endl;
                break;
            }
        }
    }
}

L - Password

真~不会

 


































以上是关于第七届山东省省赛题解的主要内容,如果未能解决你的问题,请参考以下文章

第七届山东省省赛C Proxy(最短路)

第七届山东省省赛D Swiss-system tournament(归并排序)

动态规划第七届山东省省赛 Feed the monkey

动态规划第七届山东省省赛 Feed the monkey

动态规划第七届山东省省赛 Feed the monkey

动态规划第七届山东省省赛 Feed the monkey