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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值