Codeforces Round #617 (Div. 3)

A B C D E1 E2 F
\(\checkmark\) \(\checkmark\) \(O\) \(O\) \(O\) \(O\) \(\times\)

\(\checkmark\):表明比賽時經過。c++

\(O\):表明賽後補題經過。數組

\(\times\):表明目前還未經過。函數

A. Array with Odd Sum

題目連接學習

題目大意

給你一個大小爲\(n\)的數組\(a\),每次操做能夠選擇兩個下標\(i,j\)(\(1\le i,j \le n\)\(i\ne j\)),使得\(a[i]=a[j]\),問進行有限次數的操做是否能使數組的和爲奇數。spa

解題思路

分別計數數組當中奇數和偶數的個數,會有如下幾種狀況:code

  • 只有偶數,那麼這種狀況下必定是不能夠的。
  • 只有奇數,若是數組的大小爲偶數,那麼和不可能爲奇數;反之,則能夠。
  • 既有奇數,也有偶數,這種狀況必定是能夠的。

AC代碼1

#include<bits/stdc++.h>
const int mod = 1e9+7;
const int maxn=1e5+10;
typedef long long ll;
using namespace std;
int a[maxn];
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>a[i];
        }
        bool flag1=false,flag2=false;
        for(int i=0;i<n;i++){
            if(a[i]%2==1){
                flag1=true;
            }else if(a[i]%2==0){
                flag2=true;
            }
        }
        if(!flag1){
            cout<<"NO"<<endl;
        }else {
            if(flag2){
                cout<<"YES"<<endl;
            }else{
                if(n%2==0){
                    cout<<"NO"<<endl;
                }else{
                    cout<<"YES"<<endl;
                }
            }
        }
    }
    return 0;
}

AC代碼2

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef _DEBUG
    freopen("input.txt", "r", stdin);
//  freopen("output.txt", "w", stdout);
#endif
    
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        int sum = 0;
        bool odd = false, even = false;
        for (int i = 0; i < n; ++i) {
            int x;
            cin >> x;
            sum += x;
            odd |= x % 2 != 0;
            even |= x % 2 == 0;
        }
        if (sum % 2 != 0 || (odd && even)) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    
    return 0;
}

總結

這道題就是一道思惟題,沒有什麼可說的,思惟角度不必定相同,但必定儘量的考慮全面。排序

B. Food Buying

題目連接ci

題目大意

手裏總共有\(s\)元,去超市買價格爲\(x\)(\(1\le x \le s\))的商品,會優惠\(\lfloor \frac x{10} \rfloor\)元,其中\(\lfloor \frac x {10} \rfloor\)爲向下取整,問最多消費多少元。element

解題思路

貪心思想,由於優惠是向下取整的,那麼個位數會丟掉,全部\(x\)\(10\)的倍數時,可以消費最多。字符串

AC1代碼

#include<bits/stdc++.h>
const int mod = 1e9+7;
const int maxn=1e5+10;
typedef long long ll;
using namespace std;
int main(){
    int t;
    cin>>t;
    while(t--){
        int s;
        cin>>s;
        ll res=0;
        while(s>=10){
            res+=s/10*10;
            s=s-s/10*10+s/10;
        }
        cout<<res+s<<endl;
    }
    return 0
}

AC代碼2

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef _DEBUG
    freopen("input.txt", "r", stdin);
//  freopen("output.txt", "w", stdout);
#endif
    
    int t;
    cin >> t;
    while (t--) {
        int s;
        cin >> s;
        int ans = 0;
        int pw = 1000 * 1000 * 1000;
        while (s > 0) {
            while (s < pw) pw /= 10;
            ans += pw;
            s -= pw - pw / 10;
        }
        cout << ans << endl;
    }
    
    return 0;
}

總結

簡單的貪心思惟題。

C. Yet Another Walking Robot

題目連接

題目大意

給你一個只包含「\(L,R,U,D\)」的字符串機器指令,其中\(L\)表明向左移動,\(R\)表明向右移動,\(U\)表明向上移動,\(D\)表明向下移動,其中在二維座標原點\((0,0)\)開始執行機器指令,求字符串能刪除的最短子串的下標以達到機器最終到達的二維座標點不變。

解題思路

暴力。遍歷一遍,\(pair\)存下每次到達的二維座標點,\(map\)存下對應二維座標的數組下標並擔當查詢是否出現過的功能。

AC代碼1

/*
    C題補題
*/
#include<bits/stdc++.h>
#define p pair<int,int>
#define fre(x) freopen(x,"r",stdin)
const int mod = 1e9+7;
const int maxn=1e5+10;
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int main()
{
    int t;
    // fre("data.txt");
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        string str;
        cin>>str;
        map<p,int>mp;
        //初始化位置
        mp[make_pair(0,0)]=0;
        int x=0,y=0;
        p ans=make_pair(1,1e9);
        for(int i=0;i<str.size();i++){
            if(str[i]=='L')x--;
            else if(str[i]=='R')x++;
            else if(str[i]=='U')y++;
            else if(str[i]=='D')y--;
            if(mp.find(make_pair(x,y))!=mp.end()){
                int l=mp[make_pair(x,y)],r=i+1;
                if(r-l<ans.second-ans.first){
                    ans=make_pair(l,r);
                }
            }
            mp[make_pair(x,y)]=i+1;
        }
        if(ans.second!=1e9){
            cout<<ans.first+1<<" "<<ans.second<<endl;
        }else cout<<"-1"<<endl;
    }
}

AC代碼2

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef _DEBUG
    freopen("input.txt", "r", stdin);
//  freopen("output.txt", "w", stdout);
#endif
    
    int t;
    cin >> t;
    while (t--) {
        int n;
        string s;
        cin >> n >> s;
        int l = -1, r = n;
        map<pair<int, int>, int> vis;
        pair<int, int> cur = {0, 0};
        vis[cur] = 0;
        for (int i = 0; i < n; ++i) {
            if (s[i] == 'L') --cur.first;
            if (s[i] == 'R') ++cur.first;
            if (s[i] == 'U') ++cur.second;
            if (s[i] == 'D') --cur.second;
            if (vis.count(cur)) {
                if (i - vis[cur] + 1 < r - l + 1) {
                    l = vis[cur];
                    r = i;
                }
            }
            vis[cur] = i + 1;
        }
        if (l == -1) {
            cout << -1 << endl;
        } else {
            cout << l + 1 << " " << r + 1 << endl;
        }
    }
    
    return 0;
}

總結

比賽的時候沒能想到這道題的解法,尤爲是如何去找已經出現過的點。

D. Fight with Monsters

題目連接

題目大意

\(n\)只怪獸,每一個怪獸\(i\)都有一個生命值\(h_i\),你的攻擊值是\(a\),對手的攻擊值是\(b\),對於每一隻怪獸,本身先發起攻擊,而後對手攻擊,若當前怪獸被本身打死,那麼加\(1\)分,移動到下一個怪獸。本身有\(k\)次特權,每次特權均可以讓對手當前的攻擊對怪獸無效,問本身最多能夠得多少分。

解題思路

若是當前怪獸的值對兩個攻擊者的攻擊值取餘:

  • 若餘數小於\(a\)且不爲\(0\),那麼不用特權均可以得分,移動到下一個怪獸。
  • 若餘數爲\(0\),則當怪獸剩餘生命值爲\(b\)時,將其值扔到一個數組當中。
  • 若餘數不爲\(0\)且大於\(a\),將餘數扔到數組當中。

對數組進行排序,貪心策略使用特權\(k\)已達到得分最多。

AC代碼1

#include<bits/stdc++.h>
#define p pair<int,int>
#define fre(x) freopen(x,"r",stdin)
const int mod = 1e9+7;
const int maxn=2e5+10;
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
ll num[maxn];
vector<ll>mid;
int main(){
    // fre("data.txt");
    ll n,a,b,k;
    cin>>n>>a>>b>>k;
    for(int i=0;i<n;i++){
        cin>>num[i];
    }
    ll cnt=0;
    ll sum=a+b;
    for(int i=0;i<n;i++){
        if(num[i]%sum!=0&&num[i]%sum<=a){
            cnt++;
        }else{
            mid.push_back(num[i]%sum==0?b:num[i]%sum-a);
        }
    }
    sort(mid.begin(),mid.end());
    for(int i=0;i<mid.size();i++){
        if(k*a>=mid[i]){
            cnt++;
            k-=(mid[i]/a+(mid[i]%a==0?0:1));
        }else break;
    }
    cout<<cnt<<endl;
}

AC代碼2

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef _DEBUG
    freopen("input.txt", "r", stdin);
//  freopen("output.txt", "w", stdout);
#endif
    
    int n, a, b, k;
    cin >> n >> a >> b >> k;
    vector<int> h(n);
    for (int i = 0; i < n; ++i) {
        cin >> h[i];
        h[i] %= a + b;
        if (h[i] == 0) h[i] += a + b;
        h[i] = ((h[i] + a - 1) / a) - 1;
    }
    sort(h.begin(), h.end());
    int ans = 0;
    for (int i = 0; i < n; ++i) {
        if (k - h[i] < 0) break;
        ++ans;
        k -= h[i];
    }
    
    cout << ans << endl;
    
    return 0;
}

總結

取模+貪心。

E1. String Coloring (easy version)

題目連接

題目大意

給你一個只包含小寫字母的字符串,給每個字符圖上黑色或者白色,相鄰字符串若是顏色不一樣則能夠交換位置,通過有限次數的交換,問是否有可能使得字符串變爲有序。

解題思路

題目告訴相鄰顏色不一樣的字符才能交換,換句話說就是顏色相同的字符順序是不會發生改變的,要達到最後字符串爲有序且顏色相同字符的順序不改變,那麼只有顏色相同的字符自己就是有序的,那麼問題就轉變爲字符串是否可以轉變爲兩個非遞減的子序列。用兩個字符分別標記兩個子序列的最後一個字符,若是大於兩個字符當中的任意一個,則可添加其到相應子序列的尾部。

AC代碼

/*
    E1題補題
*/
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
    string str;
    cin>>str;
    char l1='a',l2='a';
    string res;
    for(int i=0;i<str.size();i++){
        if(str[i]>=l1){
            res+="0";
            l1=str[i];
        }else if(str[i]>=l2){
            res+="1";
            l2=str[i];
        }else{
            cout<<"NO"<<endl;
            return 0;
        }
    }
    cout<<"YES"<<endl;
    cout<<res<<endl;
    return 0;
}

總結

在理解到題意以後,不要僅侷限於題目給的方向,要換角度思考問題。

E2. String Coloring (hard version)

題目連接

題目大意

題目意思和\(E1\)相似,給你一個長度爲\(n\)的字符串,給每一個字符塗上一種顏色,相鄰不一樣顏色的字符能夠交換位置,問最少須要多少種顏色使得通過有限次數的交換可讓字符串變得有序。

解題思路

思路和\(E1\)相似,去找覆蓋字符串全部字符所須要非遞減子序列的最少數量,解決方案有兩種:

  • 將字符串倒序一下,就是找覆蓋全部字符所須要非遞增子序列的個數。把每個子序列尾部的字符插入到\(set\)中,此時能夠用二分查找函數找第一個大於等於\(str[i]\)字符的字符。
  • 根據\(Dilworth\)定理能夠得知,覆蓋整個序列所需的非遞減序列的最小數量等於最長遞減子序列的長度,那麼用動態規劃找到達每一個字符的最長遞減子序列的長度,其中\(dp[i]\)表示到第\(i\)個字符\(c\)的最長遞減子序列的長度,其等於全部以大於字符\(c\)的字符結尾的最長遞減子序列長度加\(1\),最後數組\(dp\)存的就是答案。

AC代碼1

#include<bits/stdc++.h>
using namespace std;
int c[30];
vector<char>G[300];
int main()
{
    // freopen("data.txt","r",stdin);
    int n;
    cin>>n;
    string str;
    cin>>str;
    set<char>se;
    int cnt=1;
    vector<string>res;
    reverse(str.begin(),str.end());
    se.insert(str[0]);
    c[str[0]-'a']=1;
    for(int i=0;i<str.size();i++){
        auto it=se.lower_bound(str[i]);
        if(it!=se.end()){
            res.push_back(to_string(c[(*it)-'a']));
            c[str[i]-'a']=c[(*it)-'a'];
            se.erase(it);
            se.insert(str[i]);
        }else {
            se.insert(str[i]);
            cnt++;
            c[str[i]-'a']=cnt;
            res.push_back(to_string(cnt));
        }
    }
    reverse(res.begin(),res.end());
    set<string>sp;
    for(int i=0;i<res.size();i++){
        sp.insert(res[i]);
    }
    cout<<sp.size()<<endl;
    for(int i=0;i<res.size();i++){
        if(i)cout<<" ";
        cout<<res[i];
    }
    return 0;
}

AC代碼2

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef _DEBUG
    freopen("data.txt", "r", stdin);
//  freopen("output.txt", "w", stdout);
#endif
    
    int n;
    string s;
    cin >> n >> s;
    
    vector<int> maxdp(26);
    vector<int> dp(n, 1);
    for (int i = 0; i < n; ++i) {
        for (int c = 25; c > s[i] - 'a'; --c) {
            dp[i] = max(dp[i], maxdp[c] + 1);
        }
        maxdp[s[i] - 'a'] =  max(maxdp[s[i] - 'a'], dp[i]);
    }
    
    cout << *max_element(maxdp.begin(), maxdp.end()) << endl;
    for (int i = 0; i < n; ++i) cout << dp[i] << " ";
    cout << endl;
    
    return 0;
}

總結

本身的思路和標程仍是有很大差距,標程簡便不少,但總算本身可以用一種寫法寫出來。\(Dilworth\)定理以前沒有了解過,此次算是學習到了。

相關文章
相關標籤/搜索