这次训练总结是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
题意很简单,给你一个串,判断里面含多少个‘’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,继续看我的数据结构,一定不能着急。。。功课要搞好!!!