SDNU_ACM_ICPC_2021_Winter_Practice_5th [我的賽]

傳送門c++

I - Stone

題意:

唐和江進行遊戲,規則是,對於給定的n和k,唐先手,在黑板上寫出[1,k]的數,再加下來每次寫的數必須>=上次的數加1,<=上次的數加k,誰寫出的先超過n即輸數組

思路:

巴什博弈的變形。巴什博弈是n到0輸,如今是1到n輸,咱們能夠把它當作n - 1到0的巴什博弈便可,當(n - 1)% k == 0時先手輸,即江贏,不然唐贏函數

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int n, m;
    while(cin>>n>>m)
    {
        if(n == 0 && m == 0)
            break;
        else
        {
            if((n - 1) % (m + 1) == 0)
                cout<<"Jiang\n";
            else
                cout<<"Tang\n";
        }
    }
    return 0;
}

A - Strange Partition

題意:

給你n個數和x,問你這個n個數能夠進行無數次兩兩相加,問你通過操做後數組的數對k向上取整的最小值和最大值爲多少spa

思路:

對於任意一個數k,咱們能夠把它拆成兩部分看:(k / x ) * x + k % 4,也就是x的倍數加上對x的餘數,因爲是向上取整,因此餘數不管是多少都無所謂(0除外),都會進1的,因此最小值只須要咱們讓每一個數的餘數儘可能爲0便可,因此能夠把全部的值加起來,對x向上取整便可。.net

而對於最大值,就是每一個數對x向上取整,而後加起來便可。code

由於我忘了向上取整的函數是多少,因此我就用(k + x - 1)/ x 來代替函數,其結果是同樣的blog

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int IntRead()
{
    char ch = getchar();
    int s = 0, w = 1;
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
            w = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        s = s * 10 + ch - '0',
        ch = getchar();
    }
    return s * w;
}
int main()
{
    ll t, n, x;
    ll tr[100005];
    t = IntRead();
    while(t--)
    {
        memset(tr, 0, sizeof(tr));
        ll ans = 0, sum = 0, k = 0;
        n = IntRead();
        x = IntRead();
        for(int i = 1; i <= n; i++)
        {
            tr[i] = IntRead();
            sum += tr[i];//求和
            ans += (tr[i] + x - 1) / x;//對每一個數進行向上取整
        }
        ll minx = (sum + x - 1) / x;//對和進行向上取整
        ll maxn = ans;
        cout<<minx<<' '<<maxn<<'\n';
    }
    return 0;
}

H - Red and Blue

題意:

給出n+m個數,n個數與m個數是兩列數,但他們原本是一列數,問你這列數如何擺放能放下面的f(a)的值最大(這兩列數內的數相對順序不變)排序

在這裏插入圖片描述

思路:

想一下就能夠知道這個題考察的是前綴和,只需對兩堆數求前綴和便可,但記得求完前綴和與0比大小(我就這樣不當心wa了一發,逃索引

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int IntRead()
{
    char ch = getchar();
    int s = 0, w = 1;
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
            w = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        s = s * 10 + ch - '0',
        ch = getchar();
    }
    return s * w;
}
int main()
{
    ll t, n, m, ar[105],br[105],arr[105], brr[105];
    t = IntRead();
    while(t--)
    {
        ll max1 = -1e9, max2 = -1e9;
        memset(arr, 0, sizeof(arr));//初始化
        memset(brr, 0, sizeof(brr));
        memset(ar, 0, sizeof(ar));
        memset(br, 0, sizeof(br));
        n = IntRead();
        for(int i = 1; i <= n; i++)
        {
            ar[i] = IntRead();
        }
        for(int i = 1; i <= n; i++)
        {
            arr[i] = arr[i - 1] + ar[i];
            max1 = max(max1, arr[i]);//找最大前綴和
        }
        m = IntRead();
        for(int i = 1; i <= m; i++)
        {
            br[i] = IntRead();
        }
        for(int i = 1; i <= m; i++)
        {
            brr[i] = brr[i - 1] + br[i];
            max2 = max(max2, brr[i]);//一樣是找最大前綴和
        }
        max1 = max(0, max1);
        max2 = max(0, max2);
        if(max1 <= 0 && max2 >= 0)
            cout<<max2<<'\n';
        else if(max1 <= 0 && max2 <= 0)
            cout<<0<<'\n';
        else if(max1 >= 0 && max2 >= 0)
            cout<<max1 + max2<<'\n';
        else if(max1 >= 0 && max2 <= 0)
            cout<<max1<<'\n';
if(max1 <= 0 && max2 >= 0)
            cout<<max2<<'\n';
        else if(max1 <= 0 && max2 <= 0)
            cout<<0<<'\n';
        else if(max1 >= 0 && max2 >= 0)
            cout<<max1 + max2<<'\n';
        else if(max1 >= 0 && max2 <= 0)
            cout<<max1<<'\n';
        cout<<max1 + max2<<'\n';
    }
    return 0;
}

B - Strange List

題意:

給你一個n個數,和一個數x,從第一個數開始進行判斷,若是能除得盡就將整除後獲得的數按整除的份數放到這堆數的後面,一直這樣下去,最終求得這堆數的和遊戲

舉個栗子:

1 2 ##1是這個堆數原本的數量,2是除數

12 ##12 是那對數

12 6 6 3 3 3 3 ##12除2得道兩個6,因此放在那對數裏面,6除以2獲得兩個3,6除以2獲得兩個3,3除不盡2,因此中止下來

思路:

我當時想用數組來作,可是怕爆了,因此想換queue,發現不能用迭代器,還怕TLE,就另闢蹊徑:由於每次都是把除完的數全放到後面,因此一個數除完放在後面,他們的和是不變的,即12 -> 12 6 6 3 3 3 3, 6+6 = 12, 3 + 3 = 6 。至關於12 * 3。因此就沒必要循環跑,只要知道算了幾回和便可,也就是要肯定是哪裏先出現奇數,只須要單獨寫個函數,對每一個數求須要除幾回2能變成奇數便可,靠前的最小數便是咱們要找的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll tr[100005], ar[100005];
ll t, n, x, k, sum = 0;
ll f(ll y)//這是函數f(),用來求除幾回2能獲得奇數的
{
    ll len = 0;
    while(1)
    {
        if(y % x == 0)
        {
            y /= x;
            len++;
        }
        else
            break;
    }
    return len;
}
int main()
{
    cin>>t;
    while(t--)
    {
        memset(tr, 0, sizeof(tr));//由於是多組輸入,因此千萬不要忘記清0數組
        memset(ar, 0, sizeof(ar));
        sum = 0;
        ll step = 1, minx = 1e9;
        cin>>n>>x;
        for(int i = 1; i <= n; i++)
        {
            cin>>tr[i];
            sum += tr[i];//記錄一次的總和
            ar[i] = f(tr[i]);
            if(ar[i] < minx)//比較,找到位置
            {
                step = i;//記錄位置
                minx = ar[i];//更新最小值
            }
        }
        ll ans = (f(tr[step]) + 1) * sum;//+1是由於要算上剛開始的數的和
        for(int i = 1; i <= step - 1; i++)
        {
            ans += tr[i];//由於上面求的step不必定是第一個數,因此確定會加上其餘的,你寫個例子就懂了,這裏不贅述
        }
        cout<<ans<<endl;
    }
    return 0;
}

K - Period

題意:

給你一個字符串,讓你求其前綴串是不是循環串,若是是就輸出位置和循環次數

思路:

上次剛作了一個問字符串是否是循環子串的題,這個題就是她的變式,只不過此次須要循環求其前綴串,上次只須要求這個字符串,思路都是求i - next[i],看看字符串的長度可否除盡這個數便可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll next1[1000005], len1;
string s;
void getnext1()//第一個字符串的next數組的構造
{
    int j, k;
    j = 0;
    k = -1;
    next1[0] = -1;
    while(j < len1)
    {
        if(k == -1 || s[j] == s[k])
            next1[++j] = ++k;
        else
            k = next1[k];
    }
}
int main()
{

    int t, q = 1;
    while(cin>>len1 && len1)
    {
        cout<<"Test case #"<<q<<endl;
        q++;
        cin>>s;
        memset(next1, 0, sizeof(next1));
        getnext1();
        for(int i = 1; i < len1; i++)//由於題中說從第二個字符開始,因此至關於字符串的1的索引
        {//由於輸出的是字符的位置,不是從0開始的,因此要稍加改變
            int a = i + 1 - next1[i + 1];
            if((i + 1) % a == 0 && (i + 1) / a != 1)
                cout<<i + 1<<' '<<(i + 1) / a<<endl;
        }
        cout<<endl;
    }
    return 0;
}

G - Common Subsequence

題意:

求兩個字符串最大公共子序列

思路:

dp

當s[i] == ss[j],那麼對於s[i]和ss[j]的最大公共子序列就至關因而s[i - 1]與ss[j - 1]的最大公共子序列。

當s[i] != ss[j],那麼對於s[0 ……i] 和 ss[0 ……j]的最大公共子序列的值是s[0 …… i-1]與ss[0……j]的最大公共子序列的長度 或 s[0 …… i]與ss[0 …… j - 1]的最大公共子序列的長度,由於是最大,因此取最大值便可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[1005][1005];
string s, ss;
int main()
{
    while(cin>>s>>ss)
    {
        memset(dp, 0, sizeof(dp));
        int n = s.size();
        int m = ss.size();
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= m; j++)
            {
                if(s[i - 1] == ss[j - 1])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        cout<<dp[n][m]<<'\n';
    }
    return 0;
}

C - Strange Birthday Party

題意:

n個朋友,m個禮物,禮物是c[],朋友的序號是tr[]

對於每一個朋友,送的東西有兩種選擇,一是從第一個到第tr[i]個禮物裏面挑一個,二是直接送第c[i]塊錢,切記,每一個禮物只有一個,讓你花最少的錢解決全部人的東西。

思路:

貪心。由於禮物的價值的升序排列,全部能夠對朋友的序號排序,序號大的人選擇多,但直接塞錢貴,序號小的選擇少,但塞錢便宜啊,因此咱們能夠從序號大的開始循環,禮物從小的開始給,給完就換下一個,當禮物送完或者,剩下的禮物貴於直接塞錢,那就塞錢!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t, sum, k, n, m, tr[300005],c[300005];
inline int IntRead(){
    char ch = getchar();
    int s = 0, w = 1;
    while(ch < '0' || ch > '9'){
        if(ch == '-')
            w = -1;
        ch = getchar();}
    while(ch >= '0' && ch <= '9'){
        s = s * 10 + ch - '0',
        ch = getchar();}
    return s * w;
}

int main()
{
    t = IntRead();
    while(t--)
    {
        n = IntRead();
        m = IntRead();
        for(int i = 1; i <= n; i++)
            tr[i] = IntRead();
        for(int i = 1; i <= m; i++)
            c[i] = IntRead();
        sort(tr + 1, tr + 1 + n);
        k = 1;
        sum = 0;
        for(int i = n; i > 0; i--)
        {
            if(tr[i] > k)
                sum += c[k++];
            else
                sum += c[tr[i]];
        }
        cout<<sum<<endl;
    }
    return 0;
}

總結

I題妥妥的巴什博弈,我前一天還剛看過這個題,結果當時沒仔細看題,草草了事,致使今日作題明明知道作法卻由於0%x=0都不知道,樣例死都算不對,就不敢寫,後來寫出來運行後發現0%x=0,人都傻了

B題一個簡單的思惟題,開始想複雜了,後來發現不必那樣算,只須要算一下全部的和以及對每一個數向上取整的和便可

H題一個前綴和題,求兩列數的前綴和的值的和的最大值便可,記得求完前綴和不要直接相加,要看看是否是小於0!

B題也是思惟題,算一下是哪一個數最靠前且除2出現次數最小,而後將最後的和分爲兩部分算便可

K題循環子串的問題

C題貪心

G題dp求最大相同子序列

相關文章
相關標籤/搜索