1. 程式人生 > >第七屆山東省省賽題解

第七屆山東省省賽題解

color img bubuko nim 技術分享 The sort 什麽 也會

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\n",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\n",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。當一個字符串比預期的長時,它將訪問下一個數組的內存,這將導致問題並泄漏一些信息。你可以看到一個簡單的例子:
技術分享圖片

如我們所見,如果輸入字符串的長度等於或大於數組的極限長度,則額外的部分將不會被存儲,下一個數組的信息將在輸出時被泄漏。輸出將停止,直到找到“\0”字符(字符串末尾的符號)。在這個問題中,程序永遠不會有意外的結束,最後一個數組不會泄漏其他信息。
提供的源代碼如下:
技術分享圖片

INPUT
多個測試用例,第一行是整數T (T <= 20),表示測試用例的數量。
每個測試用例的第一行包含一個非空字符串,即字符串的定義,格式化為“char s1[s1_len], s2[s2_len]…”。“char”是數組的類型,它永遠不會改變。s1,s2……是數組的名稱。s1_len s2_len…長度限制。如果沒有出錯,數組應該能夠存儲輸入字符串和“\0”。不同數組的定義將用逗號和空格分開。長度限制為正,長度限制之和小於10000。
然後,將會有幾行字符串,其中包含兩到三個部分。
第一部分是“get”或“cout”,第二部分是字符串s,表示數組的名稱。s將只包含小寫字母和數字數字,並以字母開頭。在一個例子中s是不同的。如果第一部分是“get”,那麽將會有第三部分,一個字符串,該字符串應該被輸入到數組s中,輸入字符串的長度將小於1000,並且只包含可見的ASCII字符。“get”操作將重寫數組,無論之前數組中是什麽,然後在字符串後面添加“\0”。不同的部分被一個空間隔開。
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] = \0;

            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 == \n) 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] == \0) break;

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

                        printf("\n");

                        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] = \0;
                        }

                        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] == \n)
            {
                for(int j = i - 1; j >= 0; --j)
                {
                    printf("%c",a[j]);
                }
                cout << endl;
                break;
            }
        }
    }
}

L - Password

真~不會

第七屆山東省省賽題解