Manthan, Codefest 18 (rated, Div. 1 + Div. 2)----論AC的正確打開方式

前言:這場cf我當晚給忘了,事後補的,補了4題,沒有一道題是1a的,很難受,痛定思痛,發現自己的思維存在一些問題,現結合這些問題,總結ac的正確打開方式。

鏈接:http://codeforces.com/contest/1037

A題:

一開始看到tourist大神,一分鐘就a了此題,心想肯定是規律題,於是寫了前幾個之後,就草草,猜了一個規律(貌似是n+C(n,2)),結果wa了,後來就老老實實結合題意進行推導了。

其實給你n個數字,進行組合,那麼可以組合成(C(n,1)+C(n,2)+...+C(n,n)=2^n-1)個不同的數字,對於本題,只要這n個數字是連續的就可以了。

所以公式就是ans = log2(n)+1;

錯誤類型:題目理解正確,所寫即所想,數據類型範圍均合適,程序正常執行(未越界,未除零),代碼速度達已最優,所想卻爲錯。

解決方式:重新想算法。

代碼:

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin             freopen("in.txt","r",stdin)
#define Fout            freopen("out.txt","w",stdout)
#define Case(T)         int T;for(scanf("%d",&T);T--;)
#define fo(i,a,b)              for(int i = a; i < b; ++i)
#define fd(i,a,b)              for(int i = a; i >= b; --i)
#define me(a,b) memset(a,b,sizeof(a))
#define fi(a,n,val)    fill(a,a+n,val)
#define Scand(n)       scanf("%d",&n)
#define Scand2(a,b)     scanf("%d%d",&a,&b)
#define Scand3(a,b,c)     scanf("%d%d%d",&a,&b,&c)
#define Scand4(a,b,c,d)     scanf("%d%d%d%d",&a,&b,&c,&d)
#define Scans(s)       scanf("%s",s)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 1e4 + 50;
const int INF = 0xffffff;

#ifndef ONLINE_JUDGE

#endif // ONLINE_JUDGE

inline ll read(){
    ll sgn = 1; ll sum = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-')   sgn = -sgn;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        sum = sum*10+(ch-'0');
        ch = getchar();
    }
    return sgn*sum;
}

int main()
{
#ifndef ONLINE_JUDGE
    //Fin;
#endif // ONLINE_JUDGE
    ll n = read();
    ll ans = 0;
    //一開始直接模擬求解的,其實直接套公式就行
//    while (pow(2, ans) < n) {
//        ans++;
//    }
//    if((ll)pow(2, ans) == n)
//        ans++;
    ans = log2(n)+1;
    printf("%lld\n",ans);
    return 0;
}

B題:

這題tourist大神是2分鐘a的,強啊,簡直超人類啊。而我這個菜雞思考了一會,發現只要先排個序,取中間的那個,然後把它朝目標s進行加減就行,需要注意的是,當中間的那個數變爲s之後,此時數組可能已經不是從小到大的順序了,爲了讓它保持在中間位置,就需要對它前後的數字進行處理,保證在它前面的都小於等於它(如果此時它之間的數比它大,就得把這個數減到爲s),對它後面的數字進行相似的處理。

實現完代碼就交了,結果wa在第十個。。。。仔細又看了代碼,發現自己的代碼可能數組越界,少了對下標的判斷。

錯誤原因:

題目理解正確,所想也正確,所寫即所想,數據類型範圍均合適,代碼速度達已最優,程序未正常執行(越界或者除零)。

解決方式:謹防下標越界。

代碼:

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin             freopen("in.txt","r",stdin)
#define Fout            freopen("out.txt","w",stdout)
#define Case(T)         int T;for(scanf("%d",&T);T--;)
#define fo(i,a,b)              for(int i = a; i < b; ++i)
#define fd(i,a,b)              for(int i = a; i >= b; --i)
#define me(a,b) memset(a,b,sizeof(a))
#define fi(a,n,val)    fill(a,a+n,val)
#define Scand(n)       scanf("%d",&n)
#define Scand2(a,b)     scanf("%d%d",&a,&b)
#define Scand3(a,b,c)     scanf("%d%d%d",&a,&b,&c)
#define Scand4(a,b,c,d)     scanf("%d%d%d%d",&a,&b,&c,&d)
#define Scans(s)       scanf("%s",s)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 2e5 + 50;
const int INF = 0xffffff;

#ifndef ONLINE_JUDGE

#endif // ONLINE_JUDGE

inline int read(){
    int sgn = 1; int sum = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-')   sgn = -sgn;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        sum = sum*10+(ch-'0');
        ch = getchar();
    }
    return sgn*sum;
}

int arr[maxn];
ll n,s;


int main()
{
#ifndef ONLINE_JUDGE
    //Fin;
#endif // ONLINE_JUDGE
    n = read(); s = read();
    fo(i, 0, n)
        arr[i] = read();
    sort(arr, arr+n);
    ll mid = n>>1;
    ll ans = 0;
    ll ind = 0;
    if(arr[mid] == s){
        printf("%lld\n",ans);
        return 0;
    }else if(arr[mid] < s){
        ans += s-arr[mid];
        ind = mid+1;
        while (arr[ind] < s && ind < n) {        //要對ind判斷是否越界
            ans += s-arr[ind];
            ind++;
        }
    }else if(arr[mid] > s && ind >= 0){        //要對ind判斷是否越界
        ans += arr[mid]-s;
        ind = mid-1;
        while (arr[ind] > s) {
            ans += arr[ind]-s;
            ind--;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

 

C

這道題也很容易想到解法,第一個操作(交換操作)當且僅當i和j相鄰,纔會執行,否則就執行第二個操作,這是最優策略。

也是很快就寫好代碼了,交了一發,還是wa。。。。難受啊。。。

仔細分析,這個策略絕對是最優策略,不可能出錯,那麼就是代碼問題了。於是又是檢查代碼,發現在判斷執行第一個操作是,判斷條件錯了,我寫的是if(arr1[ind] != arr2[ind] && ind+1 < n && arr1[ind+1] != arr2[ind+1]),這是不對的,當arr1[ind]=arr1[ind+1]時,是不需要交換的,改成if(arr1[ind] != arr2[ind] && ind+1 < n && arr1[ind+1] != arr2[ind+1] && arr1[ind] != arr1[ind+1])就過了。

錯誤原因:

題目理解正確,所想也正確,數據類型範圍均合適,代碼速度達已最優,程序正常執行(未越界,未除零),所寫卻非所想。

解決方式:

仔細讀代碼,尋找隱藏bug。

代碼:

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin             freopen("in.txt","r",stdin)
#define Fout            freopen("out.txt","w",stdout)
#define Case(T)         int T;for(scanf("%d",&T);T--;)
#define fo(i,a,b)              for(int i = a; i < b; ++i)
#define fd(i,a,b)              for(int i = a; i >= b; --i)
#define me(a,b) memset(a,b,sizeof(a))
#define fi(a,n,val)    fill(a,a+n,val)
#define Scand(n)       scanf("%d",&n)
#define Scand2(a,b)     scanf("%d%d",&a,&b)
#define Scand3(a,b,c)     scanf("%d%d%d",&a,&b,&c)
#define Scand4(a,b,c,d)     scanf("%d%d%d%d",&a,&b,&c,&d)
#define Scans(s)       scanf("%s",s)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 1e6 + 50;
const int INF = 0xffffff;

#ifndef ONLINE_JUDGE

#endif // ONLINE_JUDGE

inline int read(){
    int sgn = 1; int sum = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-')   sgn = -sgn;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        sum = sum*10+(ch-'0');
        ch = getchar();
    }
    return sgn*sum;
}

int n;
char arr1[maxn], arr2[maxn];

int main()
{
#ifndef ONLINE_JUDGE
    //Fin;
#endif // ONLINE_JUDGE
    n = read();
    int ans = 0;
    fo(i, 0, n)
        scanf("%c",&arr1[i]);
    getchar();
    fo(i, 0, n)
        scanf("%c",&arr2[i]);
    int ind = 0;
    while (ind < n) {
        if(arr1[ind] != arr2[ind] && ind+1 < n && arr1[ind+1] != arr2[ind+1] && arr1[ind] != arr1[ind+1]){
            ans++;
            arr1[ind] = arr2[ind]; arr1[ind+1] = arr2[ind+1];
            ind += 2;
        }else if(arr1[ind] != arr2[ind]){
            ans++;
            ind++;
        }else{
            ind++;
        }
    }
    printf("%d\n",ans);
    return 0;
}

D

這題題意很簡單,就是要求你寫一個bfs,然後判斷能否輸出規定的序列。

第一次嘗試cf的D題(我菜啊。。。),這次的D題還是可以寫的,直接用bfs模擬就行,依次遍歷要輸出的序列中的字母,在當前頂點的鄰接點中find,若是找到,就繼續找下一個字母,弱若是找不到,就在隊列中下一個頂點中的鄰接點中find,最後判斷一下,是否完全輸出了規定序列就行。

一開始一直wa在第39個樣例,一直找不到wa點,後來仔細讀題才發現,題目有一行字加粗了。。。要求序列必須從1開始。。。我這是睜眼瞎啊。。。

改了之後,又交了一發,結果發現T在第48個,心想是自己的算法不對嗎,仔細分析了自己的算法,發現並不應該超時啊,仔細看了代碼,發現在查找時,用的是algothrim中的find函數,之前一直以爲庫函數中的函數複雜度都挺好的,那麼這個find函數怎麼說都應該達到O(logn)吧,結果並沒有。。。這個函數的複雜度是O(n)的,心想也對,要達到O(logn),應該要用二分,那麼二分的前提是有序,作爲適用所有結構的“通用find函數",自然不會採用二分,那麼如果用了O(n)的find,超時那是肯定的,所以得用logn的find函數,而這樣的find函數,只能調用map、set容器自帶的find函數,st.find(x)。

錯誤原因:

①題意沒看清,忽略了一些特定的條件

②超時的話,不一定算法就不行,可能是有些部分、有些函數不是很優化。

代碼:

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin             freopen("in.txt","r",stdin)
#define Fout            freopen("out.txt","w",stdout)
#define Case(T)         int T;for(scanf("%d",&T);T--;)
#define fo(i,a,b)              for(int i = a; i < b; ++i)
#define fd(i,a,b)              for(int i = a; i >= b; --i)
#define me(a,b) memset(a,b,sizeof(a))
#define fi(a,n,val)    fill(a,a+n,val)
#define Scand(n)       scanf("%d",&n)
#define Scand2(a,b)     scanf("%d%d",&a,&b)
#define Scand3(a,b,c)     scanf("%d%d%d",&a,&b,&c)
#define Scand4(a,b,c,d)     scanf("%d%d%d%d",&a,&b,&c,&d)
#define Scans(s)       scanf("%s",s)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 2e6 + 50;
const int INF = 0xffffff;

#ifndef ONLINE_JUDGE

#endif // ONLINE_JUDGE

inline int read(){
    int sgn = 1; int sum = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-')   sgn = -sgn;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        sum = sum*10+(ch-'0');
        ch = getchar();
    }
    return sgn*sum;
}

set<int> mp[maxn];
int arr[maxn], vis[maxn];
int n;
int success;
queue<int> que;

void bfs(){
    int ind = 1;
    int startt = arr[0];
    while (!que.empty()) {
        que.pop();
    }
    que.push(startt);
    vis[startt] = 1;
    while (!que.empty()) {
        int front = que.front();
        que.pop();
        while (1) {
            int aim = arr[ind];
            if(mp[front].find(aim) == mp[front].end())
                break;
            if(!vis[aim]){
                vis[aim] = 1;
                que.push(aim);
                ind++;
                if(ind == n){
                    success = 1;
                    return;
                }
            }else{
                success = 0;
                return;
            }
        }
    }
    if(ind < n)
        success = 0;
    else
        success = 1;
    return ;
}

int main()
{
#ifndef ONLINE_JUDGE
    //Fin;
#endif // ONLINE_JUDGE
    n = read();
    fo(i, 0, n-1){
        int u = read(); int v = read();
        mp[u].insert(v);
        mp[v].insert(u);
    }
    fo(i, 0, n){
        arr[i] = read();
    }
    if(arr[0] != 1){
        printf("No\n");
        return 0;
    }
    fi(vis, maxn-5, 0); success = 0;
    bfs();
    if(success)
        printf("Yes\n");
    else
        printf("No\n");
    return 0;
}

 

通過上面四題,總結了AC的正確打開方式:

①讀懂題意,不能忽略一些細節要求

②選擇合適的數據類型以及合適的存儲範圍

③選擇合適的算法,如果超時了,也不要全盤否定,可能還可以優化

④做到所寫即所想,如果wa了,查查代碼是否實現了自己所想的功能。查查是否越界啥的,或者”=“與”==“問題,初始化,除零等問題,還是wa的話,就得想想自己的算法是否正確了。

 

另外可以參看:我寫的debug總結:https://blog.csdn.net/vaeloverforever/article/details/81783932

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章