2017年11月2日训练总结

这次训练总结是10月29日到11月2日。

总体来说,做了数位DP一些稍微难一些的题目,也遇到了不。少。坑。(不到5道题,WR超过60发是什么感觉。。。)做了秦皇岛比赛的部分题目。下面很有必要写几道题的题解来提醒我注意细节的重要性。

1、HDU 3271

SNIBB

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1228    Accepted Submission(s): 318

题意很简单,定义一个SNIBB数为B进制下各位数和等于给定数N。给定一个N,B,求给定区间内第K个SNIBB数或第1个,没有输出“Could not find the Number!”。

思路:数位DP模板+二分

但是这道题对我来说有很多坑,导致WR了20多发。。。还是细节不注意,写在这里提醒自己。

1、x!可!能!小!于!y!

2、求mid时会超出int范围!要先转换成long long...

3、小于等于0要特判。。。

4、不要偷懒!!!printf("Case %d:\n",cas++);刚开始我复制前一个题的代码改,结果后面多输出了个空格没注意,WR了20多发还一脸懵逼???!!!简直绝望。。。

5、这只是一道水题。但是细节细节细节必须注意!!!

AC代码:

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<string>
#include<queue>
#include<vector>
#include<map>
using namespace std;
int f[40][310];
int a[40];
int x,y,b,m,k;
int dfs(int pos,int num,bool limit) {
   if(pos==-1) {
       return num==m;
   }
   if(!limit&&f[pos][num]!=-1) return f[pos][num];
   int i,end=limit?a[pos]:(b-1);
   int ans=0;
    for(i=0;i<=end;i++) {
        ans+=dfs(pos-1,num+i,limit&&i==end);
    }
        if(!limit) f[pos][num]=ans;
        return ans;
}
int solve(int xx)
{
    if(xx<=0) return xx==m;
    int pos=0;
    while(xx)
    {
        a[pos++]=xx%b;
        xx/=b;
    }
    return dfs(pos-1,0,1);
}
int main() {
    int t,aaa=0,kk;
    int cas=1;
    while (scanf("%d%d%d%d%d",&t,&x,&y,&b,&m)!=EOF) {
        memset(f,-1,sizeof(f));
        if(x>y) {kk=x;x=y;y=kk;}
        printf("Case %d:\n",cas++);
        if(t==1)printf("%d\n",solve(y)-solve(x-1));
        else
        {
            scanf("%d",&k);
            k+=solve(x-1);
        if(solve(y)<k) printf("Could not find the Number!\n");
        else {
        int l=x,r=y,mid,aa;
        while(l<=r)
        {
            mid=(int)(((long long)l+(long long)r)>>1);
            aa=solve(mid);
            if(aa>=k) {r=mid-1;aaa=mid;}
            else l=mid+1;
        }
        printf("%d\n",aaa);
    }
    }
    }
    return 0;
}


2、ZOJ 3985

String of CCPC

Time Limit: 1 Second       Memory Limit: 65536 KB

题意很简单,给你一个串,判断里面含多少个‘’CCPC‘’,还可以填一个‘P’或者“C”。

思路:刚开始想用KMP做,WR了将近20发,后来被告知是暴力。。。Orz

注意几个细节:

1、用过的字母还能用,但是下面找可以补成CCPC的时候就不能找之前就是完整的CCPC的。

2、三种可以补成CCPC的,就是CCC,CCP,CPC,后两种分别判断后面、前面有没有C,有的话刚才统计过了就不能用来补成CCPC,第一种CCC就不是直接判CCPC,而是判它下一位是不是P,是的话就不确定了,交给下一次的CCP判断,不是就可以补成CCPC,+1 break。(据说KMP也能做,但是我没做出来。。。)

AC代码:

#include<iostream>
#include<cmath>
#include<string>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstdio>
#include<map>
using namespace std;
const int mx=1000010;
int m,nn,ans,n;
int f[mx];
char a[mx];
int b[mx];
int main()
{
    //ios::sync_with_stdio(false);
    int T;
    int i,j,k,t;
    int ma,flag;
    //char s1[]="CPC";
    //char s2[]="CCC";
    //char s3[]="CCP";
    //char s4[]="CCPC";
    //cout<<mm[2]<<endl;
    while(scanf("%d",&n)!=EOF)
    {
        while(n--){
        ans=0;flag=0;
        scanf("%d",&nn);
        scanf("%s",a);
        for(i=0;i<=nn;i++) b[i]=0;
        for(i=0;i<nn;i++)
        {
            if(i>2&&b[i-3]==0)
            {
             if(a[i-3]=='C'&&a[i-2]=='C'&&a[i-1]=='P'&&a[i]=='C') {ans++;}
            }
        }
             for(i=2;i<nn;i++)
        {
         
             if(a[i-2]=='C'&&a[i-1]=='P'&&a[i]=='C') {if(!(i>2&&a[i-3]=='C')){ans++;break;}}
             if(a[i-2]=='C'&&a[i-1]=='C'&&a[i]=='P') {if(!(i<nn-1&&a[i+1]=='C'))
{ ans++;break;}}
             if(a[i-2]=='C'&&a[i-1]=='C'&&a[i]=='C') {if(!(i<nn-1&&a[i+1]=='P')) {ans++;break;}}
         
        }
        printf("%d\n",ans);
        }
    }
    return 0;
}

3、HDU 3565

                                     Bi-peak Number

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1243    Accepted Submission(s): 376

题意很简单,就是求给定区间内有两座峰的数的个数。

思路:数位DP。用7个数表示状态。

0:还没到第一个上坡

1:到了第一个上坡,不可以往下

2:第一个坡上,可以往下

3、正在下第一座山

4、踏上第二座山,不可以往下

5、在第二座山上,可以往下

6、第二座山的下坡,到这里就是两个峰了。
注意的细节:

这道题有几分玄学。。。用C语言过不了(不会C的64位。。。),而且它是有上下界的数位DP,开两个数组记录区间端点。

注意要用 unsigned long long!!!!!!刚开始没注意到又是蜜汁WR接近20次。

-1表示区间里没找到峰,输出0。否则直接输出结果。

C语言怎么就写不对呢???

AC代码:

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
#define INF 0x7f7f7f7f
int f[40][10][7];
int sx[40],sy[40];
unsigned long long x,y,m,kk;
int max1(int a,int b){if(a>b) return a;return b;}
int dfs(int pos,int pre,int s,int fx,int fy) {
   if(pos==-1) {return s==6?0:-1;}
   if(!fx&&!fy&&f[pos][pre][s]!=INF) return f[pos][pre][s];
   int min=fx?sx[pos]:0;
   int max=fy?sy[pos]:9;
   int ans=-1,i;
    for(i=min;i<=max;i++) {
        int ns=s;
        if(s==0&&i) ns=1;
        else if(s==1)
        {if(i>pre) ns=2;
        else ns=-1;
        }
        else if(s==2)
        {if(i<pre) ns=3;
        else if(i==pre) ns=-1;
        }
        else if(s==3&&i>=pre)
        { if(i) ns=4;
        else ns=-1;
        }
        else if(s==4)
        {if(i>pre) ns=5;
        else ns=-1;
        }
        else if(s==5)
        {if(i<pre) ns=6;
        else if(i==pre)ns=-1;
        }
        else if(s==6&&i>=pre) {ns=-1;}
        if(ns!=-1)
        {
         int sum1=dfs(pos-1,i,ns,fx&&i==min,fy&&i==max);
         if(sum1!=-1) ans=max1(ans,i+sum1);
        }
    }
        if(!fx&&!fy) f[pos][pre][s]=ans;
        return ans;
}
int main() {
    int t;
    int cas=1;
    memset(f,0x7f,sizeof(f));
    while (cin>>t) {
        while(t--)
        {
            cin>>x>>y;
             cout<<"Case "<<cas<<": ";
             cas++;
             int pos1=0;
             for(;y;x/=10,y/=10)
             {
              sx[pos1]=x%10;sy[pos1++]=y%10;
             }
             int dd=dfs(pos1-1,0,0,1,1);
             if(dd==-1) dd=0;
             cout<<dd<<endl;
    }
    }
    return 0;
}

这几天好几道题都栽在细节上了。今天在这里提醒自己以后一定要注意!!!一定要注意细节!!!(要不1A率怎么能那么低)

这几天功课比较繁重,但是依然不会放弃每天A题。。。

接下来几天再做几道有难度的数位DP,继续看我的数据结构,一定不能着急。。。功课要搞好!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值