CSP2021 J2參考解析


P7909 [CSP-J 2021] 分糖果 candy

  • 涉及知識點:閱讀理解、分支+數學思維、循環

  • 解析:本題能夠直接讀明白題目,但是要注意題目要求輸出的結果是自己單次能得到的最大值。

  • 方法1:暴力枚舉,直接將 [L, R] 進行枚舉,和現有最大值進行比較,每次取最大值。
    但是題目要求的數據范圍到 1e9,而競賽中一般認為計算機 1秒可執行的次數約在 5e8, 1e9必爆,故而該方法很遺憾只有70分,不能拿到滿分。

  • 方法2:數學思想,如果每個小朋友至少可以有 2 次選取的機會(也就是 L/n < R/n 的時候),那么最大值就是 n-1;
    否則當小朋友只有 1 次選擇的機會的時候,最大值為 R%n。
    於是,O(1)的時間復雜度就解決了這個看似很難,但也確實不容易的題目。

點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
int n,l,r;

int slove1(){
    int ans=0;
    for(int k=l; k<=r; k++){
        ans=max(ans, k%n);
    }
    return ans;
}
int slove2(){
    int ans=0;
    if(l/n < r/n) ans=n-1;
    else ans=r%n;
    return ans;
}
int main(){
    freopen("candy.in", "r", stdin);
    freopen("candy.out", "w", stdout);

    while(cin>>n>>l>>r){
        // cout<<slove1()<<endl;
        cout<<slove2()<<endl;
    }
    fclose(stdin); fclose(stdout);
    return 0;
}

P7910 [CSP-J 2021] 插入排序 sort

  • 涉及知識點:插入排序、復雜度、算法優化

  • 解析:題目較長,耐心讀完,發現題目好像不難,但是一看數據點,直接玩完,不過還是要冷靜下來動手去拿到最高的分數。

  • 方法1:純模擬,什么都不思考,就按照題目模擬一遍,當然這樣只能拿到 36~52分。

  • 方法2:將原位置排序后的現有位置記錄下來,但是還是用到了暴力排序,大概能拿到 70~80分。

  • 方法3:方法2 的基礎上優化,題目中有一個重要信息:保證類型 1 的操作次數不超過 5000,也就是說我們的主要操作會發生在...查詢上,那么考慮如何減少查詢所需要的時間。肯定是開個數組直接記錄,保證 O(1) 查詢即可,之后的時間主要是在插入元素后的排序上,那么可以對此進行優化到 O(n),這樣保證整體復雜度為 O(n*q)。
    注意:\(O(n*q)\) 不會帶入最大數據計算,因為其中有修改和查詢兩種操作,很難計算到精確值。

  • 方法5:數學思維,分桶計數,不模擬交換,計數 a[x]左邊小於 a[x] 的元素個數 l,以及 a[x]右邊大於 a[x]的元素個數 r,發現 a[x]排序后的位置為:x+r-l, 但是這樣的方法同樣會超時,畢竟每個數都需要進行一次查找,大概能拿到 70~80分。

  • 方法1 : 36分代碼

點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
struct T{
    int id, value;
}a[N], b[N];

void insertSort(T arr[], int n){
    for(int i=1; i<=n; i++)
        for(int j=i; j>=2; j--)
            if(arr[j].value < arr[j-1].value){
                T t = arr[j-1]; arr[j-1] = arr[j]; arr[j] = t;
            }
}
int find(T arr[], int n, T x){
    for(int i=1; i<=n; i++)
        if(arr[i].value==x.value && arr[i].id==x.id) return i;
    return -1;
}
bool cmp(T a, T b){
    return a.value<b.value;
}
int main(){
    freopen("sort.in", "r", stdin);
    freopen("sort.out", "w", stdout);

    int n,q; scanf("%d%d", &n, &q);
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i].value); a[i].id=i;
    }
    for(int i=1; i<=q; i++){
        int f,x,v; scanf("%d%d", &f, &x);
        if(f==1){ //修改
            scanf("%d", &v); a[x].value=v;
        }else if(f==2){ //查詢
            for(int i=1; i<=n; i++) b[i]=a[i];
            insertSort(b, n);
            printf("%d\n",find(b, n, a[x]));
        }
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 方法1: 52分代碼,在模擬的基礎上,利用 sort 稍加改進,注意穩定排序
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
struct T{
    int id, value;
}a[N];
//因為不是完全模擬插入排序 ,所以cmp發生一點小改動,需要保證穩定排序
bool cmp(T a, T b) {
    if(a.value!=b.value) return a.value<b.value;
    return a.id<b.id;
}
int main(){
    freopen("sort.in", "r", stdin);
    freopen("sort.out", "w", stdout);

    int n,q; scanf("%d%d", &n, &q);
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i].value); a[i].id=i;
    }
    sort(a+1, a+1+n, cmp);
    for(int i=1; i<=q; i++){
        int f,x,v; scanf("%d%d", &f, &x);
        if(f==1){ // 修改
            scanf("%d", &v);
            for(int j=1; j<=n; j++){
                if(a[j].id==x){
                    a[j].value=v; break;
                }
            }
            sort(a+1, a+1+n, cmp);
        }else if(f==2){ // 查詢
            for(int j=1; j<=n; j++){
                if(a[j].id==x){
                    printf("%d\n", j); break;
                }
            }
        }
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 方法2 : 70~80分代碼,將原位置的現有位置記錄下來,不用每次都去查詢
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
struct T {
    int id,value;
}a[N];
int b[N];//存放原位置的現在下標

bool cmp(T a, T b) {
    if(a.value!=b.value) return a.value<b.value;
    return a.id<b.id;
}
int main() {
    freopen("sort.in", "r", stdin);
    freopen("sort.out", "w", stdout);

    int n,q; scanf("%d%d", &n, &q);
    for(int i=1; i<=n; i++) {
        scanf("%d", &a[i].value); a[i].id=i;
    }
    sort(a+1, a+1+n, cmp);
    for(int i=1; i<=n; i++) b[a[i].id]=i;
    int f,x,v;
    for(int i=1; i<=q; i++) {
        scanf("%d%d", &f, &x);
        if(f==1) { //修改
            scanf("%d", &v); a[b[x]].value=v;
            sort(a+1, a+1+n, cmp);
            for(int i=1; i<=n; i++) b[a[i].id]=i;
        } else if(f==2) { //查詢
            printf("%d\n", b[x]);
        }
    }
    fclose(stdin); fclose(stdout);
    return 0;
}

  • 方法3:100分代碼,由於數據只有一個無序,可以考慮對排序部分進行 O(n) 優化
    image
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
int n,q;
struct T {
    int id,value;
    bool operator< (const T&temp) const {
        if(value!=temp.value) return value<temp.value;
        return id<temp.id;
    }
}a[N];
int b[N];//存放原位置的現在下標

void insertSort1(int x){ // 1次優化
    T temp=a[b[x]];
    for(int i=b[x]; i<n; i++) a[i]=a[i+1]; a[n]=temp;
    for(int i=n; i>1; i--){
        if(a[i]<a[i-1]) swap(a[i-1],a[i]);
        else break;
    }
}
void insertSort(int x){ // 2次優化
    int p=b[x];
    for(int i=b[x]-1; i>=1; i--){
        if(a[p]<a[i]) swap(a[p],a[i]),p=i;
        else break;
    }
    for(int i=b[x]+1; i<=n; i++){
        if(a[i]<a[p]) swap(a[p],a[i]),p=i;
        else break;
    }
}
int main() {
    freopen("sort.in", "r", stdin);
    freopen("sort.out", "w", stdout);

    scanf("%d%d", &n, &q);
    for(int i=1; i<=n; i++) {
        scanf("%d", &a[i].value); a[i].id=i;
    }
    sort(a+1, a+1+n);
    for(int i=1; i<=n; i++) b[a[i].id]=i;
    int f,x,v;
    for(int i=1; i<=q; i++) {
        scanf("%d%d", &f, &x);
        if(f==1) { //修改
            scanf("%d", &v); a[b[x]].value=v;
            insertSort(x);
            // sort(a+1, a+1+n);
            for(int i=1; i<=n; i++) b[a[i].id]=i;
        } else if(f==2) { //查詢
            printf("%d\n", b[x]);
        }
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 方法4:70~80分代碼,帶有一點數學思想,具體推導過程如下圖。

image

點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e4;
int a[N];
int main(){
    freopen("sort.in", "r", stdin);
    freopen("sort.out", "w", stdout);

    int n,q; cin>>n>>q;
    for(int i=1; i<=n; i++) cin>>a[i];
    for(int i=1; i<=q; i++){
        int f,x,v; cin>>f>>x;
        if(f==1){
            cin>>v; a[x]=v;
        }else{ //查詢
            int l=0,r=0;
            for(int i=1; i<x; i++)    if(a[i]>a[x]) l++;
            for(int i=x+1; i<=n; i++) if(a[i]<a[x]) r++;
            cout<<x-l+r<<endl;
        }
    }
    fclose(stdin); fclose(stdout);
    return 0;
}

P7911 [CSP-J 2021] 網絡連接 network

  • 涉及知識點:模擬

  • 解析:一道大模擬,題目不難,但是很考驗代碼量,本題需要仔細讀題,
    然后你會發現一個十分有趣的東西, 50% 的數據滿足性質 1,那么性質1 又是什么呢?

  • 就是說 ip合法,那么問題來了,本題最難的是什么?
    不就是判斷 ip地址合法嘛,我假設 ip合法,直接開始做,也能得到 50分,不香嗎?

  • 50分代碼如下

點擊查看代碼
#include<bits/stdc++.h>
#include<stdio.h>
using namespace std;
map<string, int> m;

//檢查 ip地址是否合法
bool check(string s){
    return true;
}
int main(){
    freopen("network.in", "r", stdin);
    freopen("network.out", "w", stdout);

    int n; cin>>n;
    for(int i=1; i<=n; i++){
        string op,ad;cin>>op>>ad;
        if(!check(ad)){
            cout<<"ERR"<<endl;
        }else{
            if(op=="Server"){
                if(m[ad]==0){
                    m[ad]=i; cout<<"OK"<<endl;
                }else{
                    cout<<"FAIL"<<endl;
                }
            }else if(op=="Client"){
                if(m[ad]!=0){
                    cout<<m[ad]<<endl;
                }else{
                    cout<<"FAIL"<<endl;
                }
            }
        }
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 100分代碼
    加上 ip地址合法的判斷,注意:這里我使用了 sscanf/sprintf的寫法,這里部分小伙伴沒用過,
    那么也可以使用其他寫法,我只是覺得這樣更方便,如果沒學過這部分的同學可以參考如下鏈接 https://www.cnblogs.com/hellohebin/p/15456897.html
點擊查看代碼
#include<bits/stdc++.h>
#include<stdio.h>
using namespace std;
map<string, int> m;

//檢查 ip地址是否合法
bool check(string s){
    char buf[100], buf2[100];
    for(int i=0; i<s.size(); i++) buf[i]=s[i]; buf[s.size()]='\0';
    int a,b,c,d,e;
    sscanf(buf, "%d.%d.%d.%d:%d", &a, &b, &c, &d, &e);
    sprintf(buf2, "%d.%d.%d.%d:%d", a, b, c, d, e);

//    printf("%s\n", buf); //輸出調試
//    printf("%s\n", buf2);
//    printf("%d.%d.%d.%d:%d\n", a, b, c, d, e);

    if(a<0||a>255||b<0||b>255||c<0||c>255||d<0||d>255||e<0||e>65535) return false;
    if(strcmp(buf, buf2)) return false;
    return true;
}
int main(){
    freopen("network.in", "r", stdin);
    freopen("network.out", "w", stdout);

    int n; cin>>n;
    for(int i=1; i<=n; i++){
        string op,ad;cin>>op>>ad;
        if(!check(ad)){
            cout<<"ERR"<<endl;
        }else{
            if(op=="Server"){
                if(m[ad]==0){
                    m[ad]=i; cout<<"OK"<<endl;
                }else{
                    cout<<"FAIL"<<endl;
                }
            }else if(op=="Client"){
                if(m[ad]!=0){
                    cout<<m[ad]<<endl;
                }else{
                    cout<<"FAIL"<<endl;
                }
            }
        }
    }
    fclose(stdin); fclose(stdout);
    return 0;
}

P7912 [CSP-J 2021] 小熊的果籃 fruit

  • 涉及知識點:模擬、復雜度、算法優化

  • 解析:模擬題+優化

  • 方法1:純模擬一遍,打好標記即可,但是會超時,能得 30~70分。

  • 方法2:對方法1 進行優化,主要時間浪費在查詢上面,每一次處理過的元素其實不必再次處理了,需要用到分塊/分桶的思想,開結構體,用左右元素標記塊的界限,結合隊列的方式來實現。

  • 30~70分代碼

點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N],cnt=0;
int main() {
    freopen("fruit.in", "r", stdin);
    freopen("fruit.out", "w", stdout);

    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    while (cnt<n) {
        int pre=2; // 不是 01就行
        for(int i=1; i<=n; i++) {
            if(a[i]!=-1 && pre!=a[i]) {
                printf("%d ", i);
                pre=a[i],a[i]=-1,cnt++;
            }
        } puts("");
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 這里提一下,有一個細節問題,如下程序中,使用結構體存儲數據,建議不要寫成
for(int i=1; i<=n; i++) scanf("%d", &a[i]), t[i]=T(a[i],i);

而是單獨將輸入輸出分開,可以有一定的加速。
不信可以將下面這個程序進行修改,發現會少 10分,我的天啊!竟然少了 10分。

點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N],vis[N],cnt=0;
struct T {
    int value,id;
    T(int a=0,int b=0):value(a),id(b) {}
} t[N];
int main() {
    freopen("fruit.in", "r", stdin);
    freopen("fruit.out", "w", stdout);

    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    for(int i=1; i<=n; i++) t[i]=T(a[i],i);
    while(cnt<n) {
        int pre=2; // 不是 01就行
        for(int i=1; i<=n; i++) {
            if(!vis[i] && pre!=t[i].value) {
                printf("%d ",t[i].id);
                vis[i]=1, ++cnt, pre=t[i].value;
            }
        } puts("");
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 分塊+隊列代碼,將每一個塊,放入隊列;每次對隊首元素進行處理。
    image

  • 該解法思路是正解,但是需要二次優化,如下程序在官方數據下跑了 80分。

點擊查看代碼
#include<cstdio>//企圖不用萬能頭,降低預編譯時間,然而並沒用。
#include<iostream>
#include<queue>
using namespace std;
const int N=1e6+10;
int n,a[N],vis[N],cnt=0;
struct T {
    int l,r,value;
    T(int a=0,int b=0,int c=0):l(a),r(b),value(c) {}
};
queue<T> que;
int main() {
    freopen("fruit.in", "r", stdin);
    freopen("fruit.out", "w", stdout);

    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    que.push(T(1,1,a[1]));
    for(int i=2; i<=n; i++) {
        if(que.back().value==a[i]) que.back().r=i;
        else que.push(T(i,i,a[i]));
    }
    while(que.size()) {
        int size=que.size(), pre=2;
        for(int i=1; i<=size; i++) {
            if(que.front().value!=pre) {
                printf("%d ", que.front().l);
                que.front().l++;
                pre=que.front().value;
            }
            if(que.front().l <= que.front().r) {
                que.push(que.front());
            }
            que.pop();
        } puts("");
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 滿分優化是在一個塊用完之后,出現可以合並的塊,就合並。
  • 100分代碼
點擊查看代碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N],vis[N],cnt=0,len=0;
struct T {
    int l,r,value;
    T(int a=0,int b=0,int c=0):l(a),r(b),value(c) {}
} p, t;
queue<T> que, que2;

int main() {
//    freopen("fruit.in", "r", stdin);
//    freopen("fruit.out", "w", stdout);

    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    que.push(T(1,1,a[1]));
    for(int i=2; i<=n; i++) {
        if(que.back().value==a[i]) que.back().r=i;
        else que.push(T(i,i,a[i]));
    }
    while(cnt<n) {
        while(que.size()) {
            p=que.front(), que.pop();
            while(vis[p.l] && p.l<=p.r) p.l++;
            if(p.l>p.r) continue;
            printf("%d ", p.l), cnt++;
            vis[p.l]=1, p.l++;
            if(p.l<=p.r) que2.push(p);
        } puts("");
        while(que2.size()) {
            p=que2.front(), que2.pop();
            while(que2.size()) {
                t=que2.front();
                if(p.value==t.value) p.r=t.r,que2.pop();
                else break;
            }
            que.push(p);
        }
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM