NOIP 普及组 复赛 历年 试题
NOIP 2018 普及组 复赛 试题
https://wenku.baidu.com/view/ff442eb377a20029bd64783e0912a21615797f77.html?from=search可见试题
1.标题统计 title
//NOIP 2018 普及组 复赛 第1题
//P5015 标题统计
//在线测评地址https://www.luogu.org/problemnew/show/P5015
//样例通过,提交AC。2019-3-5
#include <stdio.h>
#include <string.h>
char s[10];
int main(){
int cnt=0;
while(scanf("%s",s)!=EOF) cnt+=strlen(s);
printf("%d\n",cnt);
return 0;
}
2.龙虎斗 fight
方法二:
//NOIP 2018 普及组 复赛 第2题
//P5016 龙虎斗
//在线测评地址https://www.luogu.org/problemnew/show/P5016
//以下提供的方法适合在考场中编写,成功率高
//样例通过,提交AC。2019-3-12 21:57
#include <stdio.h>
#define LL long long
LL a[100100];
LL abs(LL a){
if(a<0)a=-a;
return a;
}
int main(){
LL n,m,p1,s1,s2,i,balance=0,min,k;
scanf("%lld",&n);
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
scanf("%lld%lld%lld%lld",&m,&p1,&s1,&s2);
for(i=1;i<=n;i++)balance+=(i-m)*a[i];
balance+=(p1-m)*s1;
k=1,min=abs((1-m)*s2+balance);
for(i=2;i<=n;i++)
if(abs((i-m)*s2+balance)<min)
min=abs((i-m)*s2+balance),k=i;
printf("%lld\n",k);
return 0;
}
方法一:
//NOIP 2018 普及组 复赛 第2题
//P5016 龙虎斗
//在线测评地址https://www.luogu.org/problemnew/show/P5016
//n=10^5,O(n^2)算法无法AC,至少要O(nlogn)算法。
//因s1,s2<10^9,int基本确定溢出,需采用long long
//样例通过,还不能提交,一些特殊情况需测试。
//测试过程中发现,有可能加入士兵过多,相比不加入 双方气势差距尽可能小
//上述这种情况,只能加入 m位置。需特判,发现后,马上添加此种情况特判
//没有完全测试完,但结束时间已到,编了60分钟,惴惴不安,提交AC。眼泪都要掉下来了。2019-3-8
#include <stdio.h>
#define maxn 100100
#define LL long long
LL a[maxn],n,m,p,s,t,q;
LL abs(LL a){
if(a<0)a=-a;
return a;
}
int main(){
LL i,Left_ans=0,Right_ans=0,delta;
scanf("%lld",&n);
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
scanf("%lld%lld%lld%lld",&m,&p,&s,&t);
a[p]+=s;//在p处,放入s士兵
for(i=1;i<m;i++)Left_ans+=(m-i)*a[i];
for(i=m+1;i<=n;i++)Right_ans+=(i-m)*a[i];
if(Left_ans==Right_ans||abs(Left_ans-Right_ans)<t){//特判,势均力敌,放在m处
printf("%lld\n",m);
}else if(Left_ans<Right_ans){//放在左侧
delta=Right_ans-Left_ans;
if(delta%t==0){//能整除
if(m<=delta/t)printf("1\n");//特判
else printf("%lld\n",m-delta/t);
}else{//不能整除
if(m<=delta/t-1)printf("1\n");//特判
else{
if(abs((delta/t+1)*t+Left_ans-Right_ans)>abs(delta/t*t+Left_ans-Right_ans))
printf("%lld\n",m-delta/t);
else
printf("%lld\n",m-(delta/t+1));
}
}
}else{//放在右侧
delta=Left_ans-Right_ans;
if(delta%t==0){
if(delta/t+m>=n)printf("%lld\n",n);
else printf("%lld\n",delta/t+m);
}else{//不能整除
if(delta/t+m>=n)printf("%lld\n",n);//特判
else{
if(abs((delta/t+1)*t+Right_ans-Left_ans)>=abs(delta/t*t+Right_ans-Left_ans))
printf("%lld\n",delta/t+m);
else
printf("%lld\n",delta/t+1+m);
}
}
}
return 0;
}
NOIP 2017 普及组 复赛 试题
https://wenku.baidu.com/view/f3fe5a326ad97f192279168884868762cbaebb71.html?from=search可见试题
1.成绩 score
//NOIP 2017 普及组 复赛 第1题
//P3954 成绩
//在线测评地址https://www.luogu.org/problemnew/show/P3954
//样例通过,提交AC。2019-3-5
#include <stdio.h>
int main(){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
printf("%d\n",a/10*2+b/10*3+c/10*5);//之前写成printf("%d\n",a*0.2+b*0.3+c*0.5);报warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’ [-Wformat=]
return 0;
}
2.图书管理员 librarian
//P3955 图书管理员
//在线测评地址https://www.luogu.org/problemnew/show/P3955
//读完题目,第一个想到的是排序
//第二个想到的是,模运算,即计算余数
//初步有了算法,但担忧超时
//再仔细看看数据范围,发现,最大n=1000,q=1000,最多耗时1000*1000=10^6
//数据设计成,不会超时
//懒得编写排序,直接动用C++中的sort函数
//编写一个功能,测试一个功能,谨慎还是必要的
//刚想着 需求码的长度 ,没啥用,突然间发现,少了 需求码的长度 还不行
// 需求码的长度 ,若为2,表示模100,若为3,表示模1000
//样例通过,是不是可以提交,还是再读读题,若无遗漏,再提交。
//读完题目,发现没什么遗漏,提交AC。2019-3-12
//评价该题,算是 普及组 复赛 第2题 中比较简单的。
//该题,耗时大约15分钟,满打满算20分钟
#include <cstdio>
#include <algorithm>
#define maxn 1010
using namespace std;
int n,q,d[maxn];
int main(){
int i,a,b,mod;
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++)scanf("%d",&d[i]);
sort(d+1,d+1+n);//默认,自小到大
while(q--){
mod=1;//初始化
scanf("%d%d",&a,&b);
for(i=1;i<=a;i++)mod*=10;
for(i=1;i<=n;i++)
if(d[i]%mod==b){
printf("%d\n",d[i]);
break;
}
if(i==n+1)printf("-1\n");//没找到书
}
return 0;
}
NOIP 2016 普及组 复赛 试题
https://wenku.baidu.com/view/c317cd8abdeb19e8b8f67c1cfad6195f312be89f.html?rec_flag=default&mark_pay_doc=2&mark_rec_page=1&mark_rec_position=2&mark_rec=view_r_1&clear_uda_param=1可见试题
1.买铅笔
https://www.luogu.org/problemnew/show/P1909可提交测评
//P1909 买铅笔
//模拟样例1
//57%2!=0 (57/2+1)*2=58
//57%50!=0 (57/50+1)*30=60
//57%30!=0 (57/30+1)*27=54
//该题是纯模拟的水题,重心在整数的整除,取余,找最值。
//有一个疑虑,int是否会溢出
//试算了一下,10000/1*10000=10^8 即需求10000只笔,每包1只,每只花费10000,故总花费10000/1*10000=10^8,没有溢出
//同时也确定了,min初始化要求min>10^8
//为了拿100分,分析还是挺关键的。
//样例通过,信心满满,但还有些忐忑,提交,哇20个测试点,结果AC 2018-3-13
#include <stdio.h>
int main(){
int n,a,b,c,min=999999999,i;
scanf("%d",&n);
for(i=1;i<=3;i++){
scanf("%d%d",&a,&b);
if(n%a!=0)c=(n/a+1)*b;//不能整除
else c=n/a*b;//能整除
if(c<min)min=c;
}
printf("%d",min);
return 0;
}
2.回文日期
https://www.luogu.org/problemnew/show/P2010可提交测评
//P2010 回文日期
//看了日期表示方式,发现正是自己平时常用的表示方式,心定了,题目读得进
//回文,发现平时也有联系
//平年,闰年,平时也有写过
//该题,将功能函数一个个编出,整合是关键
//思考如下,1将两个日期之间所有整数取出,进行回文判定,发现10^8,有超时的风险
//2将两个日期之间的符合条件的日期取出,进行回文判定,2000*365=7*10^5 不会超时
//写得过程中,发现第一年,最后一年需特殊处理
//建议,编写一个功能,测试一次,以防最后抓狂
//该题考点,分析出某个数的各个位置的值,字符串简单处理,整数取余
//样例通过,提交 AC。该题要求 程序代码 编写要比较熟练。 2018-3-13
//该题编写,新手耗时估计1小时
#include <stdio.h>
int y[3],m[3],d[3],a[3],c[]={0,31,0,31,30,31,30,31,31,30,31,30,31},cnt=0;
char s[10];
int date(int a[]){//根据 日期 解析出年 月 日 ,编的过程中,发现设想的难点:前导0没有出现
int i;
for(i=0;i<2;i++){
y[i]=a[i]/10000,a[i]%=10000;
m[i]=a[i]/100,a[i]%=100;
d[i]=a[i];
}
}
int year(int a){//闰年判定,1 闰年 2 非闰年
if(a%100==0)//整百年处理
if(a%400==0)return 1;//闰年
else return 0;
else if(a%4==0)return 1;
else return 0;
}
int check(int a){//判断回文,1是,2否
int len=0,i;
while(a)s[len++]=a%10+'0',a/=10;//将日期解析成字符串
for(i=0;i<len/2;i++)
if(s[i]!=s[len-1-i])return 0;
return 1;
}
int main(){
int i,j,k,b;//b新的日期
scanf("%d%d",&a[0],&a[1]);
date(a);
for(j=1;j<=12;j++){//生成 月 第一年处理
if(j==2)//2月单独处理
if(year(y[0]))c[j]=29;//闰年
else c[j]=28;//非闰年
for(k=1;k<=c[j];k++){//生成 日
b=10000*y[0]+j*100+k;
if(check(b))cnt++;
}
}
for(i=y[0]+1;i<y[1];i++)//生成 月 日
for(j=1;j<=12;j++){//生成 月
if(j==2)//2月单独处理
if(year(i))c[j]=29;//闰年
else c[j]=28;//非闰年
for(k=1;k<=c[j];k++){//生成 日
b=10000*i+j*100+k;
if(check(b))cnt++;
}
}
if(y[1]!=y[0])//如果没有样例1,这点容易忽略
for(j=1;j<=12;j++){//生成 月 最后一年处理
if(j==2)//2月单独处理
if(year(y[1]))c[j]=29;//闰年
else c[j]=28;//非闰年
for(k=1;k<=c[j];k++){//生成 日
b=10000*y[1]+j*100+k;
if(check(b))cnt++;
}
}
printf("%d",cnt);
return 0;
}
3.海港
https://www.luogu.org/problemnew/show/P2058可提交测评
//P2058 海港
//看了一眼数据范围,算法复杂度不能是O(n^2),而O(nlogn) 比较合适
//题目配上说明,题意还是很清楚的
//看了题目的测试点,朴素算法,还是能得70分
//仔细想想,该题属于线性序列处理,适用的方法,链表,单调栈,单调队列,树状数组,线段树
//再仔细想想,有单调性的数据,基本可用 单调队列
// 1≤n≤10^5 ∑ki≤3*10^5 困惑比较大,如何处理,内容容易溢出
//P2058 海港
//突然间想到,用前缀和更能优化
//q[100100] sum[1010]提交MLE
//q[10010] sum[1010]提交 70分,测试点7-11,13 RE 很满意 2018-3-30 21:23
//以下代码为70分代码
//从70分跨越到100分,估计要改算法,想不下去了,考虑看看题解
//https://blog.csdn.net/c20190102/article/details/53761648此文代码写得不错
//看了看,算法的区别在于,应选人为研究对象,而不是选船为研究对象,不过,一开始没想到,之后也确实想不到
//从上述角度,还是说明,算法确实不同
//样例通过,提交AC 2018-3-31 17:14
#include <stdio.h>
#include <string.h>
struct node{
int time;
int x;
}q[300100];//人
int h,t,vis[100100];
int main(){
int n,time,k,i,x,cnt=0;//此处写成 int n,time,k,i,x,cnt; 忘记将cnt初始化
memset(vis,0,sizeof(vis));
scanf("%d",&n);
h=t=1;
while(n--){
scanf("%d%d",&time,&k);
while(h<t&&q[h].time+86400<=time){
vis[q[h].x]--;
if(!vis[q[h].x])cnt--;//像不像 欧拉图 的计算
h++;
}
for(i=1;i<=k;i++){
scanf("%d",&x),q[t].time=time,q[t].x=x,t++;
if(!vis[x])cnt++;
vis[x]++;//有点像 欧拉图 的计算
}
printf("%d\n",cnt);
}
return 0;
}
//以下代码为70分代码,仅供参考
//P2058 海港
//突然间想到,用前缀和更能优化
//q[100100] sum[1010]提交MLE
//q[10010] sum[1010]提交 70分,测试点7-11,13 RE 很满意 2018-3-30 21:23
//以下代码为70分代码
#include <stdio.h>
#include <string.h>
struct node{
int time;
int k;
int sum[1010];//此处开多少,没有把握
}q[10010];
int h,t;
int main(){
int n,time,k,i,j,cnt;
memset(q,0,sizeof(q));
scanf("%d",&n);
h=t=1;
while(n--){
scanf("%d%d",&time,&k),q[t].k=k,q[t].time=time;
while(h<t&&q[h].time+86400<=time)h++;//此处写成 while(h<t&&q[h].time+86400>=time)h++;查了好久
for(i=1;i<=k;i++)scanf("%d",&j),q[t].sum[j]=1;
for(j=1;j<=1000;j++)q[t].sum[j]+=q[t-1].sum[j];
t++;
cnt=0;
for(i=1;i<=1000;i++)
if(q[t-1].sum[i]-q[h-1].sum[i])cnt++;
printf("%d\n",cnt);
}
return 0;
}
4.魔法阵
https://www.luogu.org/problemnew/show/P2119可提交测评
//P2119 魔法阵
//https://blog.csdn.net/c20181503csy/article/details/53319238此文写得比较粗
//https://blog.csdn.net/c20182030/article/details/53289443此文写得比较细
//可两篇文章,结合起来看
//需用到桶排序+排列组合知识
//样例通过,提交AC 2018-4-5 12:54
#include <stdio.h>
#include <string.h>
#define maxn 15100
#define maxm 40100
#define LL long long
LL w[maxn],h[maxm],a[maxn],b[maxn],c[maxn],d[maxn];//h[i] i存储物品序号 h[i]为物品魔法值 w[i]中i为魔法值,w[i]表示魔法值为i的物品个数 a[i]中i为魔法值 a[i]为该魔法值对应魔法阵的使用次数
LL n,m;
LL min(LL a,LL b){
return a<b?a:b;
}
LL max(LL a,LL b){
return a>b?a:b;
}
int main(){
LL i,j,x,y,min_n=16000,max_n=-1;
memset(w,0,sizeof(w));
scanf("%lld%lld",&n,&m);
for(i=1;i<=m;i++)scanf("%lld",&h[i]),w[h[i]]++,min_n=min(min_n,h[i]),max_n=max(max_n,h[i]);//此处做了优化,找出魔法值的最小值min_n,最大值max_n,之后整个程序运行时间缩短了一半
for(i=1;9*i<max_n-min_n;i++){//间距最小从1开始,自加,max_n-min_n是魔法值之间的最大间距
x=9*i+1,y=0;//x=9*i+1是a d之间的最小间距
for(j=min_n+9*i+1;j<=max_n;j++){//从D点开始枚举最小间距是9*i+1 因A B 在C D的左侧 顺序枚举
y+=w[j-x]*w[j-x+2*i];//计算a*b的组合 j-x 表示A物体的魔法值 j-x+2*i表示 B物体的魔法值 . 之前的a b影响之后的c d故,之前的a b组合数需累加
d[j]+=y*w[j-i];//d=a*b*c j-i表示 C物体的魔法值
c[j-i]+=y*w[j];//c=a*b*d j表示 D物体的魔法值
}
x=8*i+1,y=0;//注意:因C D在A B的右侧,循环需逆序 枚举,顺序不行
for(j=max_n-(9*i+1);j>=min_n;j--){//从A点开始枚举 顺序枚举不行,即for(j=min_n;j<=max_n-(9*i+1);j++)是不可以的
y+=w[j+x]*w[j+x+i];//计算c*d的组合 j+x表示C物体的魔法值,j+x+i表示D物体的魔法值. 之前的c d影响之后的a b故,之前的c d组合数需累加
a[j]+=y*w[j+2*i];//j+2*i表示B物体的魔法值,
b[j+2*i]+=y*w[j];//j表示A物体的魔法值
}
}
for(i=1;i<=m;i++)printf("%lld %lld %lld %lld\n",a[h[i]],b[h[i]],c[h[i]],d[h[i]]);//此处写成 for(i=1;i<=m;i++)printf("%d %d %d %d\n",a[h[i]],b[h[i]],c[h[i]],d[h[i]]);查了会
return 0;
}
//P2119 魔法阵
//试了 普及组的 深浅
//发现 没有超出 范围 的 未掌握的 知识点
//该题,若是比赛时,也没想着拿满分,只要有分拿,即可
//先快排
//开一个数组,存储对应序号的顺序,如q[i]=j,表示 自小到大 排在 第i位 元素 对应的 序号是 j 该思想意味着水平的大幅提升
//https://blog.csdn.net/ssl_zzy/article/details/53308193比较喜欢该文的写法
//测试了一下,发现,样例1,遗漏 物品1,3,7,6,其魔法值分别为1,7,26,29;
//经测试,发现7-1=6 26-7=19 19/3=6
//故,需要将除改成 乘
//即xb-xa<(xc-xb)/3 改成 3*(xb-xa)<(xc-xb)
//预测 四重 循环,能到测试点 7
//提交,发现,结果,不是WA ,就是TLE
//重新读题,发现"保证标准输出中的每个数都不会超过10^9","每行相邻的两个数之间用恰好一个空格隔开"
//果断改成long long并按题目要求,改写输出格式
//提交,发现,结果,不是WA ,就是TLE
//if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j]&&x[i]<x[j]&&x[j]<x[k]&&x[k]<x[p])//此处写成 if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j])确实忘了==情况的判定
//提交,测试点4-11 TLE 测试点12 TLE 得分55 2018-4-2 18:45
#include <stdio.h>
#include <string.h>
int q[40100],n,m;
long long x[40100],cnt[40100][5];
void quicksort(int left,int right){//自小到大
int i=left,j=right,t1;
long long mid=x[(left+right)/2],t2;
while(i<=j){
while(x[i]<mid)i++;
while(x[j]>mid)j--;
if(i<=j){
t2=x[i],x[i]=x[j],x[j]=t2;
t1=q[i],q[i]=q[j],q[j]=t1;
i++,j--;
}
}
if(left<j)quicksort(left,j);
if(i<right)quicksort(i,right);
}
int main(){
int i,j,k,p;
memset(cnt,0,sizeof(cnt));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)scanf("%lld",&x[i]),q[i]=i;
quicksort(1,m);
for(i=1;i<=m;i++)
for(j=i+1;j<=m;j++)
for(k=j+1;k<=m;k++)
for(p=k+1;p<=m;p++)
if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j]&&x[i]<x[j]&&x[j]<x[k]&&x[k]<x[p])//此处写成 if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j])确实忘了==情况的判定
cnt[q[i]][1]++,cnt[q[j]][2]++,cnt[q[k]][3]++,cnt[q[p]][4]++;
for(i=1;i<=m;i++){
for(j=1;j<=4;j++)
if(j==1)printf("%lld",cnt[i][j]);
else printf(" %lld",cnt[i][j]);
printf("\n");
}
return 0;
}
NOIP 2015 普及组 复赛 试题
https://wenku.baidu.com/view/33e7b2b252d380eb63946d2b.html可见试题
1.金币
https://www.luogu.org/problemnew/show/P2669可提交测评
//P2669 金币
//因k<=10000;每天收到10000枚,10000天,共计10000*10000=10^8,很明显int够用了
//并且,该题 纯模拟 即可
//循环次数,1+2+3+...+n=(1+n)*n/2 n^2=10000极限n=100次
//样例通过,
//自个编了几组测试样例,
//样例
//1
//1
//样例
//2
//3
//样例
//3
//5
//样例
//4
//8
//样例
//5
//11
//样例
//7
//18
//均顺利通过,放心了,提交AC 2018-4-5 15:00
#include <stdio.h>
int main(){
int k,i=1,sum=0,cnt=0;
scanf("%d",&k);
while(1){
sum+=i*i;
cnt+=i;//天数
if(cnt>k)break;
i++;
}
cnt-=i,sum-=i*i;//回退,保证cnt<=k//多出天数的处理
sum+=(k-cnt)*i;//之后的第i天处理,天数是k-cnt,金币,每天是i
printf("%d",sum);
}
2.扫雷游戏
https://www.luogu.org/problemnew/show/P2670可提交测评
//P2670 扫雷游戏
//题目,还没有到广度优先遍历的难度
//具体就是枚举任意一点,若该点是地雷,就不做任何事,若是非地雷就枚举8个方向,统计地雷数
//有广度优先遍历的基础,该题就是一道水题。
//样例通过,提交AC 2018-4-5 15:33
//采用字符串方式读取雷区
#include <stdio.h>
char s[110][110];
int n,m,d[][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};//上 下 左 右 左上 右上 左下 右下
int main(){
int i,j,k,nr,nc,cnt;
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)scanf("%s",s[i]);
for(i=0;i<n;i++){//基本思路,边处理 边打印
for(j=0;j<m;j++)
if(s[i][j]=='*')printf("*");//地雷
else {
cnt=0;
for(k=0;k<8;k++){
nr=i+d[k][0],nc=j+d[k][1];//八个方向枚举
if(0<=nr&&nr<n&&0<=nc&&nc<m&&s[nr][nc]=='*')
cnt++;
}
printf("%d",cnt);
}
printf("\n");
}
return 0;
}
3.求和
https://www.luogu.org/problemnew/show/P2671可提交测评
//P2671 求和
//看了一样10007基本可以确定是质数,简单编码做了检验,发现确实是质数
//检验代码如下
//看了数据范围,O(n^2)超时,要么O(n),要么O(nlogn)
//有了NOIP 2016 普及组 魔法阵 的基础后 该题就容易了
//看看int是否会溢出,极限情况取一个1 n/2 n 三元组 (1+100000)*(100000+100000)=2*10^10很明显int溢出,采用long long
//样例通过,看了看数据范围,发现仅能通过前四组测试数据,得分40
//提交,确实如此,测试点2,6-10RE,得分40
//只按序号处理,40分,只按颜色处理,猜测60分 100000*20=2*10^6
//以下为40分代码
//测算了5组,6组 枚举20*20*100000/20=2*10^6刚好不超时
//只要同种原色间的差值是偶数,即为符合条件的x,z
//样例通过,提交,测试点2,8-10 TLE ,果然60分,很是欣慰 2018-4-5 19:27
//以下为60分代码 与 40分 代码进行对比,确实是改算法了
//100分,AC的算法究竟长什么样。一窥真容,还是突然开窍。
//翻看了100分的算法,发现,确实想不到,
//奇偶性,这个想法在60分代码里有体现,不过,没有深入下去 奇+奇=偶 奇+偶=奇 偶+偶=偶
//将表达式,乘开,动过想法,但发现没什么用,
//归根结底,还是要多历练。
//此文思路写得不错https://www.luogu.org/problemnew/solution/P2671
根据题目,我们可以设有三个下标,他们分别是x,y,z,要满足x<y<z且y-x=z-y
所以我们可以模拟x,y,z,其时间复杂度为O(n^3),由于n<=100000所以一定会超时
但这个方法是可以优化的
由此我们可以得到2y=z+x
由此可知z+x必须是一个偶数即z,x同为奇数或同为偶数
所以我们其实只要穷举x和z就行了,但这个方法的时间复杂度为O(n^2)所以还是会超时
我们可以用一下分组思想,把每个颜色分为一组,再在每个颜色中按奇偶分组,所以一共有2m组
设一个分组里有k个数,这个分组中的数分别是x[1],x[2]……x[k],下标分别是y[1],y[2]……y[k]
那么可得
答案=(x[1]+x[2])*(y[1]+y[2])+(x[1]+x[3])*(y[1]+y[3])+……+(x[1]+x[k])*(y[1]+y[k])
+(x[2]+x[3])*(y[2]+y[3])+(x[2]+x[4])*(y[2]+y[4])+……+(x[2]+x[k])*(y[2]+y[k])
+……
+(x[k-1]+x[k])*(y[k-1]+y[k])
=x[1]*(y[1]+y[2]+y[1]+y[3]+……+y[1]+y[k])
+x[2]*(y[1]+y[2]+y[2]+y[3]+……+y[2]+y[k])
+……
+x[k]*(y[1]+y[k]+y[2]+y[k]+……+y[k-1]+y[k])
=x[1]*(y[1]*(n-2)+y[1]+y[2]+……+y[k])
+x[2]*(y[2]*(n-2)+y[1]+y[2]+……+y[k])
+……
+x[k]*(y[k]*(n-2)+y[1]+y[2]+……+y[k])
然后事先将y[1]+y[2]+……+y[k]求出,用的时候调用就行了,其时间复杂度为O(n)
//样例通过,提交AC 2018-4-6 9:51
#include <stdio.h>
#include <string.h>
#define maxn 100100
#define LL long long
#define mod 10007
LL n,m,number[maxn],color[maxn],sum[maxn][2],c[maxn][2];
int main(){
LL i,ans=0,y;
memset(sum,0,sizeof(sum)),memset(c,0,sizeof(c));
scanf("%lld%lld",&n,&m);
for(i=1;i<=n;i++)scanf("%lld",&number[i]);
for(i=1;i<=n;i++){
scanf("%lld",&color[i]);
c[color[i]][i%2]++;
sum[color[i]][i%2]=(sum[color[i]][i%2]+number[i])%mod;
}
for(i=1;i<=n;i++){
y=((c[color[i]][i%2]-2)*number[i]+sum[color[i]][i%2])%mod;
ans=(ans+i*y)%mod;//为了增加程序的可读性,将拆成两行来处理
}
printf("%lld",ans);
return 0;
}
//P2671 求和
//看了一样10007基本可以确定是质数,简单编码做了检验,发现确实是质数
//检验代码如下
//看了数据范围,O(n^2)超时,要么O(n),要么O(nlogn)
//有了NOIP 2016 普及组 魔法阵 的基础后 该题就容易了
//看看int是否会溢出,极限情况取一个1 n/2 n 三元组 (1+100000)*(100000+100000)=2*10^10很明显int溢出,采用long long
//样例通过,看了看数据范围,发现仅能通过前四组测试数据,得分40
//提交,确实如此,测试点2,6-10RE,得分40
//只按序号处理,40分,只按颜色处理,猜测60分 100000*20=2*10^6
//以下为40分代码
//测算了5组,6组 枚举20*20*100000/20=2*10^6刚好不超时
//只要同种原色间的差值是偶数,即为符合条件的x,z
//样例通过,提交,测试点2,8-10 TLE ,果然60分,很是欣慰 2018-4-5 19:27
//以下为60分代码 与 40分 代码进行对比,确实是改算法了
#include <stdio.h>
#define maxn 100100
#define LL long long
#define mod 10007
LL n,m,number[maxn],color[maxn][110];//对color[maxn][110]中的110惴惴不安,似乎应该开得更大,不过,容易MLE,color[i][j] 第i种颜色 对应的 第j个原色 对应的序号
int main(){
LL i,ans=0,x,z,j,k;
scanf("%lld%lld",&n,&m);
for(i=1;i<=n;i++)scanf("%lld",&number[i]),color[i][0]=0;
for(i=1;i<=n;i++)scanf("%lld",&k),color[k][++color[k][0]]=i;//用color[i][0]用来存储对应的颜色的数量
for(i=1;i<=m;i++)
for(j=1;j<=color[i][0];j++)
for(k=j+1;k<=color[i][0];k++){
x=color[i][j],z=color[i][k];
if((z-x)%2==0)ans=(ans+(x+z)*(number[x]+number[z]))%10007;
}
printf("%lld",ans);
return 0;
}
//P2671 求和
//看了一样10007基本可以确定是质数,简单编码做了检验,发现确实是质数
//检验代码如下
//看了数据范围,O(n^2)超时,要么O(n),要么O(nlogn)
//有了NOIP 2016 普及组 魔法阵 的基础后 该题就容易了
//看看int是否会溢出,极限情况取一个1 n/2 n 三元组 (1+100000)*(100000+100000)=2*10^10很明显int溢出,采用long long
//样例通过,看了看数据范围,发现仅能通过前四组测试数据,得分40
//提交,确实如此,测试点2,6-10RE,得分40
//只按序号处理,40分,只按颜色处理,猜测60分 100000*20=2*10^6
//以下为40分代码
#include <stdio.h>
#define maxn 100100
#define LL long long
#define mod 10007
LL n,m,number[maxn],color[maxn];
int main(){
LL i,ans=0,x,z;
scanf("%lld%lld",&n,&m);
for(i=1;i<=n;i++)scanf("%lld",&number[i]);
for(i=1;i<=n;i++)scanf("%lld",&color[i]);
for(i=1;2*i<=n-1;i++)//i=y-x=z-y i是间距
for(x=n-2*i;x>=1;x--){
z=x+2*i;
if(color[x]==color[z]){
ans=(ans+(x+z)*(number[x]+number[z]))%10007;
}
}
printf("%lld",ans);
return 0;
}
//P2671 求和
//看了一样10007基本可以确定是质数,简单编码做了检验,发现确实是质数
//检验代码如下
#include <stdio.h>
int main(){
int i;
for(i=2;i*i<=10007;i++)
if(10007%i==0)printf("i=%d",i);
return 0;
}
4.推销员
https://www.luogu.org/problemnew/show/P2672可提交测评
//P2672 推销员
//第一直觉是动态规划
//如何处理距离,与推销 疲劳
//仔细想想,无论走过多少户,距离由最远的决定
//f[i][j] 选j个数,在i个里选
//数据范围,2*10^8+10^3*10^5 int应该够用
//拿出纸笔模拟,开始手忙脚乱,不过很快就适应了,该题对背包要求理解深刻
//在模拟的过程中,发现需开两个数组 f[maxn][maxn],g[maxn][maxn];//f包括路径的疲劳度 g不包括路径的疲劳度
//样例通过,估计能有60分
//保存好上述代码,开始将二维数组降为一维数组
//按01背包,一维数组方式,顺序循环,始终样例未通过
//果断按,完全背包的方式,逆序循环,样例通过,怎么想到的?不知道,直觉告诉,说,可以这样试试
//提交,测试点2,8-10TLE 还是60分,动归到头了
//一窥题解,
//此文代码写得够短https://www.luogu.org/problemnew/solution/P2672?&page=2思路摘抄如下
//这可能是一个最短的代码:
//看到这道题首先想到dp或贪心
//因为这是一道普及组的题,所以这题贪心就可以(滑稽)
//考虑我们如何将答案最大化:对于每个x,一定是选择(一个最大的s)+(x-1个最大的a)或者x个最大的a,可以使答案最优
//那么具体怎么实现呢
//我们先把数组按照a排序
//我们用sum[i]表示a数组的前i项的和,q[i]表示s数组的前i项的最大值,h[i]表示a[i]*2+s[i]后i项的最大值,对于每个x,他的答案就是max(sum[x]+2*q[x],sum[x-1]+h[x])
//读完该题思路,以及代码,感觉真是神了,怎么想到的呢,还是离不开样例数据的模拟,观察,以及大胆的猜想。
//样例通过,提交AC 2018-4-7 17:14
#include <stdio.h>
#define maxn 100100
int s[maxn],a[maxn],sum_a[maxn],b[maxn],d[maxn],ans[maxn];//b[i]剩下部分最大的2*s[i]+a[i]
int max(int a,int b){
return a>b?a:b;
}
void quicksort(int left,int right){//自大到小排序
int i=left,j=right,mid=a[(left+right)/2],t;
while(i<=j){
while(a[i]>mid)i++;
while(a[j]<mid)j--;
if(i<=j){
t=a[i],a[i]=a[j],a[j]=t;
t=s[i],s[i]=s[j],s[j]=t;
i++,j--;
}
}
if(i<right)quicksort(i,right);
if(left<j)quicksort(left,j);
}
int main(){
int n,i;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&s[i]);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
quicksort(1,n);
sum_a[0]=0,b[n+1]=0,d[0]=0;
for(i=1;i<=n;i++)sum_a[i]=sum_a[i-1]+a[i];//sum_a[i]前i个最大a的和
for(i=n;i>=1;i--)b[i]=max(b[i+1],2*s[i]+a[i]);
for(i=1;i<=n;i++)d[i]=max(d[i-1],s[i]);//1->i中的最远距离
for(i=1;i<=n;i++)ans[i]=max(sum_a[i-1]+b[i],sum_a[i]+2*d[i]);
for(i=1;i<=n;i++)printf("%d\n",ans[i]);
return 0;
}
//P2672 推销员
//第一直觉是动态规划
//如何处理距离,与推销 疲劳
//仔细想想,无论走过多少户,距离由最远的决定
//f[i][j] 选j个数,在i个里选
//数据范围,2*10^8+10^3*10^5 int应该够用
//拿出纸笔模拟,开始手忙脚乱,不过很快就适应了,该题对背包要求理解深刻
//在模拟的过程中,发现需开两个数组 f[maxn][maxn],g[maxn][maxn];//f包括路径的疲劳度 g不包括路径的疲劳度
//样例通过,估计能有60分
//开始将二维数组降为一维数组
//在降维过程中,顺便将二维数组代码提交,确实60分,测试点2,8-10 RE 很是满意。
//以下为60分代码,二维数组的01背包问题.
#include <stdio.h>
#include <string.h>
#define maxn 1100
int a[maxn],b[maxn],f[maxn][maxn],g[maxn][maxn];//f包括路径的疲劳度 g不包括路径的疲劳度
int main(){
int n,i,j;
memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);//a[i]距离
for(i=1;i<=n;i++)scanf("%d",&b[i]);//b[i]推销疲劳度
for(i=1;i<=n;i++)//在i个物品里选
for(j=1;j<=n;j++){//选j个物品
if(f[i-1][j]<g[i-1][j-1]+a[i]*2+b[i])
f[i][j]=g[i-1][j-1]+a[i]*2+b[i];
else f[i][j]=f[i-1][j];
if(g[i-1][j]<g[i-1][j-1]+b[i])
g[i][j]=g[i-1][j-1]+b[i];
else g[i][j]=g[i-1][j];
}
for(i=1;i<=n;i++)printf("%d\n",f[n][i]);
return 0;
}
//P2672 推销员
//第一直觉是动态规划
//如何处理距离,与推销 疲劳
//仔细想想,无论走过多少户,距离由最远的决定
//f[i][j] 选j个数,在i个里选
//数据范围,2*10^8+10^3*10^5 int应该够用
//拿出纸笔模拟,开始手忙脚乱,不过很快就适应了,该题对背包要求理解深刻
//在模拟的过程中,发现需开两个数组 f[maxn][maxn],g[maxn][maxn];//f包括路径的疲劳度 g不包括路径的疲劳度
//样例通过,估计能有60分
//保存好上述代码,开始将二维数组降为一维数组
//按01背包,一维数组方式,顺序循环,始终样例未通过
//果断按,完全背包的方式,逆序循环,样例通过,怎么想到的?不知道,直觉告诉,说,可以这样试试
//提交,测试点2,8-10TLE 还是60分,动归到头了
//一窥题解,
#include <stdio.h>
#include <string.h>
#define maxn 100100
int a[maxn],b[maxn],f[maxn],g[maxn];//f包括路径的疲劳度 g不包括路径的疲劳度
int max(int a,int b){
return a>b?a:b;
}
int main(){
int n,i,j;
memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);//a[i]距离
for(i=1;i<=n;i++)scanf("%d",&b[i]);//b[i]推销疲劳度
for(i=1;i<=n;i++)//在i个物品里选
for(j=n;j>=1;j--){//选j个物品
f[j]=max(f[j],g[j-1]+a[i]*2+b[i]);
g[j]=max(g[j],g[j-1]+b[i]);
}
for(i=1;i<=n;i++)printf("%d\n",f[i]);
return 0;
}
NOIP 2014 普及组 复赛 试题
https://wenku.baidu.com/view/8c054b6b844769eae109ed3b.html可见试题
1.珠心算测验
https://www.luogu.org/problemnew/show/P2141可提交测评
//P2141 珠心算测验
//该题编写方法很多
//第一步,将数据自小到大排序
//第二步,二重循环,计算和
//第三步,在数据里查找,是否有等于该和的数
//上述算法,时间复杂度O(n^3),不会超时
//不过,觉得不舒服,立马想到桶排序
//将第三步,开一个10000的数组,和与数,直接在桶排序里进行判别,时间复杂度,立马变成O(n^2)
//10000+10000,计算数值不会超出int
//仔细想想,该题,一个桶排序,即可解决。
//样例通过,提交,测试点2,4,6-10WA不敢想象
// 重读一遍题目,发现"注意,加数和被加数必须是集合中的两个不同的数。" ,说实话,该句有歧义
//是指 两个数不相等,还是指集合中两个不同位置的数
//重新读题,发现"正整数集合,集合中的数各不相同"
//http://codevs.cn/problem/3459/也有该题,可查看无法通过的数据
//看了测试数据,才发现,关键之处在于“其中有多少个数,恰好等于集合中另外两个(不同的)数之和?”
//统计个数,不过,题意不明啊,考试时如何将这个意思读出,真是很困难,
//将上述样例给出,
//输入:
100
6094 106 5958 877 3352 8959 292 9253 3860 448 5709 8295 4667 2588 3663 2486 1129 8870 3126 6203 727 4316 6541 9689 8818 2119 7331 7598 1879 7303 1100 8740 4567 2649 2373 7505 5848 4337 9953 4436 8395 8273 1028 4449 3363 6254 7856 6710 8422 3833 5690 6664 6326 2550 119 2075 3475 9024 3239 8889 807 543 8103 1441 9731 3873 5194 9557 3256 6245 3266 2777 4552 2390 8550 4777 9701 5694 9133 6736 2586 298 3695 1992 2958 3831 6905 8134 989 6292 8967 118 8442 1765 931 4760 5945 1703 6262 1671
//输出:
22
//说明,如8889=4337+4552 8889=5194+3695 但,统计只能统计1次
//样例通过,终于,提交AC 2018-4-7 22:19
//对该题的意见:如何能让 被测试者 读出题中意思,才是关键,该题难的不是代码,而是语义理解。
//个人认为,该题表述糟糕,难以让人抓住重点。
#include <stdio.h>
#include <string.h>
int a[110],b[10100];
int main(){
int n,i,j,cnt=0;
memset(b,0,sizeof(b));
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]),b[a[i]]=1;
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
if(a[i]+a[j]<=10000&&b[a[i]+a[j]]==1)
cnt++,b[a[i]+a[j]]=0;
printf("%d",cnt);
return 0;
}
2.比例简化
https://www.luogu.org/problemnew/show/P2118可提交测评
//P2118 比例简化
//看完题目,算法就跳出来了
//枚举+欧几里德算法+浮点数计算(注意精度)
//样例通过,提交,测试点4,7WA
//问题出在浮点数运算上。看来还是要定义一个0
//在http://codevs.cn/problem/3460/提交,看到了一组错误的数据
//输入:
//199 2 100
//输出:
//100 1
//else 归谁管,遇到困惑, 测试下来结果是,与最近的if配对
//样例通过,提交AC 2018-4-8 10:46
#include <stdio.h>
#include <math.h>
#define eps 1e-8
int gcd(int a,int b){//欧几里德算法
return b?gcd(b,a%b):a;
}
int main(){
int a,b,L,i,j,c,d;
double delta,min=999;
scanf("%d%d%d",&a,&b,&L);
for(i=1;i<=L;i++)
for(j=1;j<=L;j++)
if(gcd(i,j)==1){//互质
delta=fabs(i*1.0/j-a*1.0/b);//注意取绝对值,因浮点数计算存在误差
if(delta>eps){//大于0
if(i*1.0/j-a*1.0/b>eps&&min>delta)//只处理A’/B’ ≥ A/B
min=delta,c=i,d=j;
}
else//<=0
if(min>delta)
min=delta,c=i,d=j;
}
printf("%d %d",c,d);
return 0;
}
3.螺旋矩阵
https://www.luogu.org/problemnew/show/P2239可提交测评
//P2239 螺旋矩阵
//第一想法,将矩阵格子填充完毕(类广度优先遍历的方式进行填充,发现高端算法在普及组受限的比较多,基本都要超时),再进行查询,算法复杂度O(n^2),超时基本没问题
//纯枚举方式,只有50分,
//归根结底,还是要找出公式
//画图,观察,发现,每圈上元素个数为4*n-4 n>=2 ; 1 n=1
//每圈需2行2列,
//基本思路:看当前节点属于矩阵的第几圈,第几个数,之后即可算出
//30000*30000=9*10^8 int不越界
//将矩阵一圈一圈拆除,同时注意加上拆除的元素个数
//样例通过,并且测试了n=1,n=2,n=5 矩阵中的每个元素都测试通过,算法时间复杂度O(n)
//提交AC,2018-4-8 18:17
#include <stdio.h>
int main(){
int n,r,c,cnt=0;
scanf("%d%d%d",&n,&r,&c);
while(1){
if(r==1||r==n||c==1||c==n){//在当前圈内 该花括号里的计算过程,建议读者画图理解
if(r==1)cnt+=c;
else if(r==n)cnt+=n+n-1+n-c;//n横 n-1竖 n-c横
else if(c==n)cnt+=n+r-1;
else if(c==1)cnt+=n+n-2+n+n-2-(r-2);
break;
}
cnt+=4*n-4,r--,c--,n-=2;//此处写成n--//拆除该圈
}
printf("%d",cnt);
}
4.子矩阵
https://www.luogu.org/problemnew/show/P2258可提交测评
//P2258 子矩阵
//https://www.cnblogs.com/WQHui/p/5869500.html此文思路和代码都写得不错,摘抄如下:
//这题如果按暴力做只有一半分,最大时间复杂度为O(C(16,8)*C(16,8)); 很容易算出超时;
//我们可以发现如果直接dp会很难想,但是知道选哪几行去dp就很好写状态转移方程:
// dp[i][j]=min(dp[i][j],dp[k][j-1]+a[i]+b[k][i]);
//其中dp[i][j]表示 前i列里选j列的子矩阵最小分值,注意,必需包含i列
// a[i]表示 第i列选到的行的总差值
// b[k][i]表示选到的每一行第k列和第i列的差值,注意,不包含k列与i列之间的列
//我们只要枚举 行 然后dp一次,取最小值即可 这样最大时间复杂度就成了O(C(8,16)*n3);
//样例通过,提交AC 2018-4-21 22:31
#include <stdio.h>
#include <string.h>
#define maxn 20
#define INF 999999999
int n,m,r,c,a[maxn][maxn],row[maxn],vis[maxn],min_sub=INF,zc[maxn],hc[maxn][maxn],f[maxn][maxn];//zc[i]纵差 即选定的r行中,第i列上,对应的行差的和 hc[i][j]即 选定的r行中 第i列 与 第j列 的 行差和
int abs(int x){
if(x<0)x=-x;
return x;
}
int min(int a,int b){
return a<b?a:b;
}
void dp(){
int i,j,k;
memset(zc,0,sizeof(zc)),memset(hc,0,sizeof(hc)),memset(f,0,sizeof(f));//f[i][j] 前i列中 选j列,包括第i列 对应的最小值
for(i=1;i<=m;i++)
for(j=1;j<r;j++)
zc[i]+=abs(a[row[j+1]][i]-a[row[j]][i]);
for(i=1;i<=m;i++)
for(j=i+1;j<=m;j++)
for(k=1;k<=r;k++)
hc[i][j]+=abs(a[row[k]][j]-a[row[k]][i]);
for(i=1;i<=m;i++)
for(j=1;j<=min(i,c);j++){//j<=min(i,c)表示 既要 j<=i 又要 j<=c
f[i][j]=INF;
for(k=j-1;k<=i-1;k++)
f[i][j]=min(f[i][j],f[k][j-1]+zc[i]+hc[k][i]);//此处写成f[i][j]=min(f[i][j],f[k][j-1]+zc[j]+hc[k][j]);
}
for(i=c;i<=m;i++)
if(f[i][c]<min_sub)min_sub=f[i][c];
}
void dfs_row(int step){//枚举行
int i;
if(step==r+1){//选中r行
dp();
return ;
}
for(i=row[step-1]+1;i<=n;i++)
if(vis[i]==0){
vis[i]=1;
row[step]=i;
dfs_row(step+1);
vis[i]=0;
}
}
int main(){
int i,j;
scanf("%d%d%d%d",&n,&m,&r,&c);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
memset(vis,0,sizeof(vis)),row[0]=0;
dfs_row(1);//1表示第一步
printf("%d",min_sub);
return 0;
}
//P2258 子矩阵
//先试枚举,拿分是关键
//采用深搜,枚举行,之后枚举列
//P1008 三连击 有该题基础应该能拿些分数
//程序编好,样例1,样例2通过,估计能有50分,提交,55分,测试点3,5-11,13 TLE,很是满意。2018-4-19
//以下代码为55分代码
//C(12,6)*C(12,6)*6*6=3.1*10^7 介于超时与不超时之间
//C(16,8)*C(16,8)*8*8=1.0*10^10 看来确实要超时了
//故 深搜+回溯 也就到头了,要AC ,只能改算法了。
#include <stdio.h>
#include <string.h>
int a[20][20],n,m,r,c,r2[20],c2[20],vis_r[20],vis_c[20],pre=1,b[20][20],score,min_sub=999999999;//b[][]存新数组数据
void dfs_col(int step);//函数声明
int abs(int x){
if(x<0)x=-x;
return x;
}
void dfs_row(int step){//枚举行
int i;
if(step==r+1){
dfs_col(1);
return ;
}
for(i=r2[step-1]+1;i<=n;i++)
if(vis_r[i]==0){
vis_r[i]=1;
r2[step]=i;
dfs_row(step+1);
vis_r[i]=0;
}
}
void dfs_col(int step){//枚举列
int i,j,k,sub=0;
if(step==c+1){
for(i=1;i<=r;i++)//将选出的行列对应节点数据放入新的矩阵,方便处理
for(j=1;j<=c;j++)
b[i][j]=a[r2[i]][c2[j]];
for(i=1;i<=r;i++)//行处理
for(j=1;j<c;j++)
sub+=abs(b[i][j+1]-b[i][j]);
for(j=1;j<=c;j++)
for(i=1;i<r;i++)//列处理
sub+=abs(b[i+1][j]-b[i][j]);
if(min_sub>sub)min_sub=sub;
return ;
}
for(i=c2[step-1]+1;i<=m;i++)//此处写成for(i=1;i<=c;i++),昏了
if(vis_c[i]==0){
vis_c[i]=1;
c2[step]=i;
dfs_col(step+1);
vis_c[i]=0;
}
}
int main(){
int i,j,p,q;
memset(vis_r,0,sizeof(vis_r)),memset(vis_c,0,sizeof(vis_c)),r2[0]=0,c2[0]=0;
scanf("%d%d%d%d",&n,&m,&r,&c);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
dfs_row(1);
printf("%d",min_sub);
}
NOIP 2013 普及组 复赛 试题
https://wenku.baidu.com/view/aae243f1d1f34693daef3e6d.html?from=search可见试题
1.计数问题
https://www.luogu.org/problemnew/show/P1980可提交测评
//P1980 计数问题
//测算统计的数字个数 (1+1000000)*1000000/2=5*10^12
//每个数字,最多7个数,故统计范围7*5*10^12=3.5*10^13 int溢出,需采用 long long
//样例通过,提交AC 2018-4-22 9:05
//仔细想想,上述计算有误,极限数字个数1000000,每个数字,最多7位数,故极限统计1000000*7=7*10^6故int 溢出
//马上修改,提交AC 2018-4-22 9:55
#include <stdio.h>
int cnt=0,x;
void count(int n){//将n解析成一个一个的单个数字
while(n){
if(n%10==x)cnt++;
n/=10;
}
}
int main(){
int n,i;
scanf("%d%d",&n,&x);
for(i=1;i<=n;i++)
count(i);
printf("%d",cnt);
return 0;
}
2.表达式求值
https://www.luogu.org/problemnew/show/P1981可提交测评
//P1981 表达式求值
//"所有参与运算的数字均为 0 到 2^31-1 之间的整数" 粗想是采用long long
//再细细一想,每个数运算前,先对10000取模,再进行计算,那么int 也就不会溢出了
//解析出数字,解析出运算符号,
//运算符100000,参与运算数字个数100000+1,每个数字最长位数10,故,字符串长度(100000+1)*10+100000=1100001
//该题,说白了,就是字符串处理
//遇到*先处理,第一遍扫描,处理完*运算
//之后,剩下的数据加加起来就好了。
//样例通过,提交AC 2018-4-22 11:48
#include <stdio.h>
#include <string.h>
#define mod 10000
char s[1100100];
int a[110000];
int main(){
int i,len,k=0,flag=0,ans=0;//flag *标记 1 否则为0
memset(a,0,sizeof(a));
scanf("%s",s);
len=strlen(s);
for(i=0;i<len;i++)
if(s[i]=='+'){
if(flag==0)
k++;
else{
a[k-1]=a[k]%mod*(a[k-1]%mod)%mod;
a[k]=0;
flag=0;
}
}else if(s[i]=='*'){
if(flag==0){
flag=1;//漏了此句,查了会
k++;
}
else{
a[k-1]=a[k]%mod*(a[k-1]%mod)%mod;
a[k]=0;
flag=1;
}
}
else{
a[k]*=10;
a[k]+=s[i]-'0';
}
//字符串扫描结束之后,还要对最后两个数字进行判定
if(flag==1){
a[k-1]=a[k]%mod*(a[k-1]%mod)%mod;
a[k]=0;
}else//保证a[k]==0;
k++;
for(i=0;i<k;i++)//剩下的数,加加起来即可
ans=(ans+a[i])%mod;
printf("%d",ans);
return 0;
}
3.小朋友的数字
https://www.luogu.org/problemnew/show/P1982可提交测评
//该题,测试点数据有可能超越long long范畴,或者说long long有可能溢出,证明如下:
//提供一组测试数据给大家
//输入:
5 981
-409 -401 97 -96 -301
//输出:
-409
以上述数据为例
特征值为
-409 -401 97 97 97
分数为
-409 -818 -818 -721 -624
//P1982 小朋友的数字
//读题数十遍,发现,这句始终没读进"输出时保持最大值的符号,将其绝对值对 p 取模后输出。"
//不得不说,这句真的很难懂,很难懂"其它小朋友的分数为排在他前面的所有小朋友中(不包括他本人),小朋友分数加上其特征值的最大值"
//以下代码为80分代码,2018-4-25 21:50
//https://blog.csdn.net/sunshinezff/article/details/45271411此文思路写得不错,摘抄如下:
主要考察语文能力,关键把题读懂。
读完题就能发现这是个(几乎是裸的)最大子段和问题。对于最大子段和问题,我们有O(N)的算法。 具体的做法是这样的:当前要求第I位及之前的最大子段和,如果第(I-1)位及之前的最大子段和大于0,则显然这一位取了也未尝不可(不会减少),也就是当前这一位和前面一段连接起来。否则的话,就新开一段——把前面的最大子段和改成0以后继续往下扫描。 如果一定要说这是DP也可以。
这样朴素的做能得50分,
在计算特征值与分数的过程中记录一下最大值可以的得到80分
原因在与最后两个点的分数值超过了longlong。
进一步分析可以发现除了第一个小朋友外剩下的小朋友的分数值是不下降的。所以对于一个小朋友他的分数只有两种情况。
1.如果他的前一个小朋友的特征值大于0,那他的分数就为前一个小朋友的分数加上前一个小朋友的特征值。更新当前最大值。
2.如果他的前一个小朋友的特征值小于0,那他的分数就为第二个小朋友的分数。
当一个小朋友的分数值大于1000000000时我们取模
因为第一个小朋友的分数不会大于1000000000,所以我们就可以在计算过程中判断出来是否有小朋友的分数大于第一个小朋友。
这样就可以拿到满分。
//以下为AC代码,2018-4-25 22:48
#include <stdio.h>
#define LL long long
#define maxn 1000100
LL f[maxn],a[maxn],t[maxn],score[maxn];//t[i]为特征值
LL max(LL a,LL b){
return a>b?a:b;
}
int main(){
LL n,p,i,MAX,ans,flag=0;//flag判断是否后面的数大于score[1]
scanf("%lld%lld",&n,&p);
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
t[1]=MAX=f[1]=a[1];//此处写成t[i]=MAX=f[1]=a[1]; 查了好久
for(i=2;i<=n;i++){
f[i]=max(f[i-1]+a[i],a[i]);
MAX=max(MAX,f[i]);
t[i]=MAX;
}
score[1]=t[1],score[2]=t[1]+score[1];
for(i=3;i<=n;i++){
if(t[i-1]<=0)score[i]=score[i-1];//此处写成 if(t[i]<=0)score[i]=score[i-1]; 查了好久好久,最后一处难查的错误
else score[i]=score[i-1]+t[i-1];//此处写成 score[i]=score[i-1]+t[i]; 昏招
if(score[i]>1000000000)flag=1;//score[1]<=1000000000故 if(score[i]>1000000000)flag=1;
if(flag==1)score[i]%=p;
}
if(flag==1)ans=score[n];
else ans=score[1]>score[n]?score[1]:score[n];
printf("%lld",ans%p);
return 0;
}
//P1982 小朋友的数字
//读题数十遍,发现,这句始终没读进"输出时保持最大值的符号,将其绝对值对 p 取模后输出。"
//不得不说,这句真的很难懂,很难懂"其它小朋友的分数为排在他前面的所有小朋友中(不包括他本人),小朋友分数加上其特征值的最大值"
//以下代码为80分代码,2018-4-25 21:50
#include <stdio.h>
#define LL long long
#define maxn 1000100
LL f[maxn],a[maxn],t[maxn],score[maxn];//t[i]为特征值
LL max(LL a,LL b){
return a>b?a:b;
}
int main(){
LL n,p,i,MAX,ans;
scanf("%lld%lld",&n,&p);
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
t[1]=MAX=f[1]=a[1];//此处写成t[i]=MAX=f[1]=a[1]; 查了好久
for(i=2;i<=n;i++){
f[i]=max(f[i-1]+a[i],a[i]);
MAX=max(MAX,f[i]);
t[i]=MAX;
}
score[1]=t[1],score[2]=t[1]+score[1];
for(i=3;i<=n;i++)score[i]=max(score[i-1]+t[i-1],score[i-1]);
ans=score[1]>score[n]?score[1]:score[n];
if(ans<0)printf("-%lld",-ans%p);
else printf("%lld",ans%p);
return 0;
}
//P1982 小朋友的数字
//"其它小朋友的分数为排在他前面的所有小朋友中(不包括他本人),小朋友分数加上其特征值的最大值"该句难懂,借助说明才搞懂
//"其他数字的绝对值均不超过 10^9"该句告诉我们,int不够用了,要上long long
//1 ≤ n ≤ 1,000,000 算法的时间复杂度仅限于O(n),O(nlogn),而O(n^2)已不适用
//对于 50%的数据,1 ≤ n ≤ 1,000 第一目标50分,基本的算法,枚举,应能达成目标
//第一步,傻傻的算 b[i][j] i->j之间的和,发现算法复杂度是O(n^3),代码如下
//for(i=1;i<=n;i++)
// for(j=1;j<=n;j++)
// for(k=i;k<=j;k++)
// b[i][j]+=a[k];
//立马放弃,回忆起,学过前缀和,是该用的时候了
//sum_max[i] 1->i中 最大的连续和 计算原理 如下:
//sum_max[1]: sum[1]=a[1]
//sum_max[2]: sum[1] sum[2] sum[2]-sum[1] 找最大值
//sum_max[3]: sum[1] sum[2] sum[2]-sum[1] sum[3] sum[3]-sum[1] sum[3]-sum[2]找最大值
//sum_max[4]: sum[1] sum[2] sum[2]-sum[1] sum[3] sum[3]-sum[1] sum[3]-sum[2] sum[4] sum[4]-sum[1] sum[4]-sum[2] sum[4]-sum[3]找最大值
//样例通过,提交,测试点1 WA,测试点2,7-10 RE ,得分40,还算满意 2018-4-23
//将 #define maxn 1100 改成 #define maxn 1000100 ,提交50分,测试点1 WA,测试点2,8-10 RE 很是满意
//以下代码为50分代码
#include <stdio.h>
#include <string.h>
#define LL long long
#define maxn 1000100
#define INF 2000000000
LL a[maxn],sum[maxn],sum_max[maxn],score[maxn];//sum_max[i] 1->i中 最大的连续和
LL max(LL a,LL b){
return a>b?a:b;
}
int main(){
LL n,p,i,j,k,ans=-INF;
sum[0]=0;
scanf("%lld%lld",&n,&p);
for(i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
sum_max[1]=a[1];
for(i=2;i<=n;i++){//该功能是靠手动模拟得出的
sum_max[i]=sum_max[i-1];
for(j=0;j<i;j++)//此处写成 for(j=1;j<i;j++)查了会,即漏了sum[i]的比较
sum_max[i]=max(sum_max[i],sum[i]-sum[j]);
}
score[1]=sum_max[1];
for(i=2;i<=n;i++)score[i]=max(score[i-1],score[i-1]+sum_max[i-1]);
for(i=1;i<=n;i++)ans=max(ans,score[i]);
printf("%lld",ans%p);
return 0;
}
4.车站分级
https://www.luogu.org/problemnew/show/P1983可提交测评
// P1983 车站分级
//模拟了数据,发现是图,用到 入度 ,入度为0的 站点 属同一级别,将其去除,新的入度为0的点属于较低的一个级别,如此递推
//看了数据范围,邻接矩阵 可用
//编写的过程中,发现读取数据,进行rd 计算时,程序会超时, 不过50分应该拿到了
//样例通过,提交,测试点8 TLE,得分90,超乎想象,很满意。2018-4-27
//以下代码为90分代码。
//该题需要AC,有一个至关重要的地方,若是采用邻接矩阵,入度处理 应放在停靠站点上rd[a[j]]++,若放在非停靠站点上rd[k]++,该题始终无法AC,测试点8 始终TLE
//处理图时,
for(k=a[1]+1;k<=a[si]-1;k++)
for(j=1;j<=si;j++)
if(vis[k]==0&&map[k][a[j]]==0)
map[k][a[j]]=1,rd[a[j]]++;
//写成
for(k=a[1]+1;k<=a[si]-1;k++)
if(vis[k]==0)
for(j=1;j<=si;j++)
if(map[k][a[j]]==0)
map[k][a[j]]=1,rd[a[j]]++;
//仅是锦上添花,并未参与是否AC
//2018-4-27 19:00 AC 该题,发现上述问题,用了相当长的时间
//以下为AC代码
#include <stdio.h>
#include <string.h>
#define maxn 1100
int map[maxn][maxn],a[maxn],vis[maxn],rd[maxn],q[maxn];//q[i]队列
int main(){
int n,m,i,j,si,k,cnt=0,h,t,x;
memset(map,0,sizeof(map)),memset(rd,0,sizeof(rd));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){//读取数据
memset(a,0,sizeof(a)),memset(vis,0,sizeof(vis));
scanf("%d",&si);
for(j=1;j<=si;j++)
scanf("%d",&a[j]),vis[a[j]]=1;//此处写成 vis[j]=1 昏招
for(k=a[1]+1;k<=a[si]-1;k++)
if(vis[k]==0)
for(j=1;j<=si;j++)
if(map[k][a[j]]==0)
map[k][a[j]]=1,rd[a[j]]++;
}
memset(vis,0,sizeof(vis));//此句需写在 循环while(1)之外
while(1){
h=t=1;
for(i=1;i<=n;i++)
if(vis[i]==0&&rd[i]==0)
vis[i]=1,q[t]=i,t++;
if(h==t)break;//无入度为0的点,跳出循环
while(h<t){
x=q[h];
for(i=1;i<=n;i++)//去除 入度为0 的点 对应的连线
if(map[x][i])
map[x][i]=0,rd[i]--;
h++;
}
cnt++;
}
printf("%d",cnt);
return 0;
}
//尝试了邻接表,发现 入度 选择 非停靠的站 ,测试点8还是TLE
//无奈,还是将 入度 选择 停靠的站 ,提交,耗时确实大幅降低。
//很遗憾,该题若 入度 选择 非停靠的站 90分, 入度 选择 停靠的站 100分
//考场上十全十美很难,有时缺憾也是一种美,90分,可以接受。2018-4-27 21:08
//以下代码为, 入度 选择 停靠的站,邻接表 AC 代码
#include <stdio.h>
#include <string.h>
#define maxn 1100
int map[maxn][maxn],a[maxn],vis[maxn],rd[maxn],q[maxn],head[maxn],cnt=0;//q[i]队列
struct node{
int to;
int next;
}e[maxn*maxn/2];
void add(int u,int v){
cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
int main(){
int n,m,i,j,si,k,ans=0,h,t,x,b,v;
memset(map,0,sizeof(map)),memset(rd,0,sizeof(rd)),memset(head,0,sizeof(head));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){//读取数据
memset(a,0,sizeof(a)),memset(vis,0,sizeof(vis));
scanf("%d",&si);
for(j=1;j<=si;j++)
scanf("%d",&a[j]),vis[a[j]]=1;//此处写成 vis[j]=1 昏招
for(k=a[1]+1;k<=a[si]-1;k++)
if(vis[k]==0)
for(j=1;j<=si;j++)
if(map[k][a[j]]==0)
map[k][a[j]]=1,add(k,a[j]),rd[a[j]]++;
}
memset(vis,0,sizeof(vis));//此句需写在 循环while(1)之外
while(1){
h=t=1;
for(i=1;i<=n;i++)
if(vis[i]==0&&rd[i]==0)
vis[i]=1,q[t]=i,t++;
if(h==t)break;//无入度为0的点,跳出循环
while(h<t){
x=q[h];
b=head[x];
while(b){
v=e[b].to;
rd[v]--;
b=e[b].next;
}
h++;
}
ans++;
}
printf("%d",ans);
return 0;
}
// P1983 车站分级
//以下为采用 邻接表+记忆化搜索 AC代码,很遗憾对 入度 选取有要求,停靠站点计算入度(codevs,洛谷)均能AC;非停靠站点计算入度,(codevs无法AC 测试点7 TLE)洛谷能AC。
//2018-4-28 17:30 AC
#include <stdio.h>
#include <string.h>
#define maxn 1100
int map[maxn][maxn],a[maxn],vis[maxn],rd[maxn],head[maxn],cnt=0,d[maxn];//d[i] i点深度
struct node{
int to;
int next;
}e[maxn*maxn/2];
void add(int u,int v){
cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
int max(int a,int b){
return a>b?a:b;
}
int dfs(int u){
int b,v;
if(d[u]){//记忆化搜索
return d[u];
}
d[u]=1,b=head[u];
while(b){
v=e[b].to;
d[u]=max(d[u],dfs(v)+1);
b=e[b].next;
}
return d[u];
}
int main(){
int n,m,i,j,si,k,ans=0,h,t,x,b,v;
memset(map,0,sizeof(map)),memset(rd,0,sizeof(rd)),memset(head,0,sizeof(head)),memset(d,0,sizeof(d));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){//读取数据
memset(a,0,sizeof(a)),memset(vis,0,sizeof(vis));
scanf("%d",&si);
for(j=1;j<=si;j++)
scanf("%d",&a[j]),vis[a[j]]=1;//此处写成 vis[j]=1 昏招
for(k=a[1]+1;k<=a[si]-1;k++)
if(vis[k]==0)
for(j=1;j<=si;j++)
if(map[k][a[j]]==0)
map[k][a[j]]=1,add(k,a[j]),rd[a[j]]++;
}
for(i=1;i<=n;i++)
if(rd[i]==0)
ans=max(ans,dfs(i));
printf("%d",ans);
return 0;
}
// P1983 车站分级
//模拟了数据,发现是图,用到 入度 ,入度为0的 站点 属同一级别,将其去除,新的入度为0的点属于较低的一个级别,如此递推
//看了数据范围,邻接矩阵 可用
//编写的过程中,发现读取数据,进行rd 计算时,程序会超时, 不过50分应该拿到了
//样例通过,提交,测试点8 TLE,得分90,超乎想象,很满意。2018-4-27
//以下代码为90分代码。
//看了些AC代码,发现本人数据读取这块还是写得挺漂亮的,TLE 问题出在 一层一层的 处理 入度为0的点上
//看了些AC代码,采用栈 ,发现是 开 两个栈,这样效率确实高,触类旁通,开两个队列
//修改,提交,测试点8,还是TLE
//以下代码仍为90分代码。2018-4-27
#include <stdio.h>
#include <string.h>
#define maxn 1100
int map[maxn][maxn],a[maxn],vis[maxn],rd[maxn],q1[maxn],q2[maxn];//q[i]队列
int main(){
int n,m,i,j,si,k,cnt=0,h1,t1,x,h2,t2;
memset(map,0,sizeof(map)),memset(rd,0,sizeof(rd));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){//读取数据
memset(vis,0,sizeof(vis));
scanf("%d",&si);
for(j=1;j<=si;j++)
scanf("%d",&a[j]),vis[a[j]]=1;//此处写成 vis[j]=1 昏招
for(j=1;j<=si;j++)
for(k=a[1]+1;k<=a[si]-1;k++)//此处写成 for(k=a[j]+1;k<=a[si]-1;k++) 昏招
if(vis[k]==0&&map[a[j]][k]==0)//此处写成 if(vis[k]==0) 请注意,不要重复计算入度
map[a[j]][k]=1,rd[k]++;
}
memset(vis,0,sizeof(vis));//此句需写在 循环while(1)之外
h1=t1=1;
for(i=1;i<=n;i++)
if(vis[i]==0&&rd[i]==0)
vis[i]=1,q1[t1]=i,t1++;
while(1){
h2=t2=1;
if(h1==t1)break;//无入度为0的点,跳出循环
else{
while(h1<t1){
x=q1[h1];
for(i=1;i<=n;i++)//去除 入度为0 的点 对应的连线
if(map[x][i]){
rd[i]--;
if(rd[i]==0)
q2[t2]=i,t2++;
}
h1++;
}
memcpy(q1,q2,sizeof(q2)),h1=h2,t1=t2;
cnt++;
}
}
printf("%d",cnt);
return 0;
}
// P1983 车站分级
//模拟了数据,发现是图,用到 入度 ,入度为0的 站点 属同一级别,将其去除,新的入度为0的点属于较低的一个级别,如此递推
//看了数据范围,邻接矩阵 可用
//编写的过程中,发现读取数据,进行rd 计算时,程序会超时, 不过50分应该拿到了
//样例通过,提交,测试点8 TLE,得分90,超乎想象,很满意。2018-4-27
//以下代码为90分代码。
#include <stdio.h>
#include <string.h>
#define maxn 1100
int map[maxn][maxn],a[maxn],vis[maxn],rd[maxn],q[maxn];//q[i]队列
int main(){
int n,m,i,j,si,k,cnt=0,h,t,x;
memset(map,0,sizeof(map)),memset(rd,0,sizeof(rd));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){//读取数据
memset(a,0,sizeof(a)),memset(vis,0,sizeof(vis));
scanf("%d",&si);
for(j=1;j<=si;j++)
scanf("%d",&a[j]),vis[a[j]]=1;//此处写成 vis[j]=1 昏招
for(j=1;j<=si;j++)
for(k=a[1]+1;k<=a[si]-1;k++)//此处写成 for(k=a[j]+1;k<=a[si]-1;k++) 昏招
if(vis[k]==0&&map[a[j]][k]==0)//此处写成 if(vis[k]==0) 请注意,不要重复计算入度
map[a[j]][k]=1,rd[k]++;
}
memset(vis,0,sizeof(vis));//此句需写在 循环while(1)之外
while(1){
h=t=1;
for(i=1;i<=n;i++)
if(vis[i]==0&&rd[i]==0)
vis[i]=1,q[t]=i,t++;
if(h==t)break;//无入度为0的点,跳出循环
else{
while(h<t){
x=q[h];
for(i=1;i<=n;i++)//去除 入度为0 的点 对应的连线
if(map[x][i])
rd[i]--;
h++;
}
cnt++;
}
}
printf("%d",cnt);
return 0;
}
NOIP 2012 普及组 复赛 试题
https://wenku.baidu.com/view/0a99b694daef5ef7ba0d3c77.html?from=search可见试题
1.质因数分解
https://www.luogu.org/problemnew/show/P1075可提交测评
//P1075 质因数分解
//样例通过,提交AC 2018-4-28 21:01
#include <stdio.h>
int isPrime(int n){//判断是否是质数
int i;
if(n==1)return 0;
if(n==2)return 1;
for(i=2;i*i<=n;i++)
if(n%i==0)
return 0;
return 1;
}
int main(){
int n,i;
scanf("%d",&n);
for(i=2;i<=n;i++)
if(isPrime(i)&&n%i==0){
printf("%d",n/i);
break;
}
return 0;
}
2.寻宝
https://www.luogu.org/problemnew/show/P1076可提交测评
//P1076 寻宝
//该题原题请看https://wenku.baidu.com/view/0a99b694daef5ef7ba0d3c77.html?from=search
//洛谷还是有些信息遗漏,如 输入输出样例 说明
//样例通过,提交,只有20分,剩下的全是WA
//多次修改,觉得找不出问题了,可惜,结果如前
//无奈,下载测试数据,
//下载之后发现,错误答案对应的输入数据,数据量太大,肉眼无法完成识别
//看来,测试数据还是无用
//无奈之下,开始阅读AC代码,https://www.cnblogs.com/geek-007/p/4661702.html
//还是无用,思路很不同
//静态阅读代码,突然发现int d[maxn];//此处写成d[maxm],就是此处,查了整整一天,Dev-C++4.9.9.2确实有Bug,数组开得太小,编译器尽然查不出啊
//错误真是不起眼,归根结底,编的时候太匆忙,查的时候要还的,而且这个错误,确实得靠自己,才能改过来。
//样例通过,提交AC 2018-5-2 20:55
#include <stdio.h>
#include <string.h>
#define maxn 10100
#define maxm 110
#define mod 20123//此处写成 #define mod 20213
int n,m,a[maxn][maxm],b[maxn][maxm],c[maxn][maxm],d[maxn];//此处写成d[maxm]就是此处,查了整整一天,Dev-C++4.9.9.2确实有Bug,数组开得太小,编译器尽然查不出啊//a[][]记录是否有楼梯 b[][]指示牌上数据 c[i][j]表示第i层第j+1个有楼梯的房号
int main(){
int i,j,start,ans=0,x;//ans计算密钥
scanf("%d%d",&n,&m);
for(i=0;i<n;i++){
d[i]=0;
for(j=0;j<m;j++){
scanf("%d%d",&a[i][j],&b[i][j]);
if(a[i][j])c[i][d[i]++]=j;
}
}
scanf("%d",&start);
for(i=0;i<n;i++){
x=b[i][start];
if(a[i][start]){//if与else 语句可以合并,但为了读者的可读性,就不合并了
for(j=0;j<d[i];j++)
if(start==c[i][j])
break;
start=c[i][(j+x-1)%d[i]];
}else{
for(j=0;j<d[i];j++)
if(start<c[i][j])//if(start<c[i][j])此处会出问题,若start比所有的有楼梯的房间序号都大,那么这个判断是有问题的
break;
if(j<d[i])//此句添加
start=c[i][(j+x-1)%d[i]];//此处写成start=c[i][((j-1)+x)%d[i]+1];//此处写成 start=c[i][((j-1)+x)%mod+1];
else //此句添加
start=c[i][(0+x-1)%d[i]];//此处写成start=c[i][1];一招臭棋//此句添加
}
ans=(ans+x)%mod;//该题比较奇特,x参与了两次 模 一次 模c[i][0],另一次 模20213(注,应是 模 20123)
}
printf("%d",ans);
return 0;
}
3.摆花
https://www.luogu.org/problemnew/show/P1077可提交测评
//P1077 摆花
//第一遍粗读完题目的感觉是,动态规划
//看完题目,发现是完全背包
//此种题目特点,要对数据量小的时候的数据,进行完全的测试。
//之后,才能提交,这样能保证成功率
//以样例为例,数据如下
//f[1][0]=1 f[1][1]=1 f[1][2]=1 f[1][3]=1 f[1][4]=0
//f[2][0]=1 f[2][1]=2 f[2][2]=3 f[2][3]=3 f[2][4]=2
//样例通过,提交,AC 2018-5-3
//此类题目,小数据量的模拟,十分重要。
//第一次看到 裸的动态规划 问题 在 普及组 出现
#include <stdio.h>
#include <string.h>
#define mod 1000007
int n,m,f[110][110],a[110];
int main(){
int i,j,k;
memset(f,0,sizeof(f));
f[0][0]=1;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
for(i=1;i<=n;i++)
for(j=0;j<=m;j++)//此处写成 for(j=1;j<=m;j++)
for(k=0;k<=a[i];k++)
if(j>=k)
f[i][j]=(f[i][j]+f[i-1][j-k])%mod;
printf("%d",f[n][m]);
return 0;
}
4.文化之旅
https://www.luogu.org/problemnew/show/P1078可提交测评
//P1078 文化之旅
//2≤N≤100 Floyd 算法即可
//2≤N≤100 1≤d≤1000 100*1000=10^5 int即可
//该题的难点在于,数据的预处理。
//for(k=1;k<=N;k++)//此处写成 for(k=1;i<=N;k++) 昏招,导致提交,只有10分 功亏一篑
//修改,提交,codevs AC,洛谷 80分,测试点11,12 WA,很明显,洛谷增加数据强度了
//那么该题就不再深入下去了。2018-5-3
#include <stdio.h>
#define maxn 110
#define INF 999999999
int c[maxn],a[maxn][maxn],e[maxn][maxn];
int main(){
int N,K,M,S,T,i,j,k,d;
scanf("%d%d%d%d%d",&N,&K,&M,&S,&T);
for(i=1;i<=N;i++)scanf("%d",&c[i]);
for(i=1;i<=K;i++)
for(j=1;j<=K;j++)
scanf("%d",&a[i][j]);
for(i=1;i<=N;i++)
for(j=1;j<=N;j++)
if(i==j)e[i][j]=0;
else e[i][j]=e[j][i]=INF;
for(k=1;k<=M;k++){
scanf("%d%d%d",&i,&j,&d);
e[i][j]=e[j][i]=d;
}
//文化相斥,路径设置为INF 有向图
for(i=1;i<=N;i++)
for(j=1;j<=N;j++)
if(a[c[i]][c[j]])//aij= 1 表示文化 i 排斥外来文化 j 即 j->i 路径为INF
e[j][i]=INF;
for(k=1;k<=N;k++)//此处写成 for(k=1;i<=N;k++) 昏招,导致提交,只有10分 功亏一篑
for(i=1;i<=N;i++)
for(j=1;j<=N;j++)
if(e[i][j]>e[i][k]+e[k][j])
e[i][j]=e[i][k]+e[k][j];
if(e[S][T]==INF)printf("-1");
else printf("%d",e[S][T]);
return 0;
}
NOIP 2011 普及组 复赛 试题
https://wenku.baidu.com/view/669b6c1c227916888486d742.html可见试题
1.数字反转
https://www.luogu.org/problemnew/show/P1307可提交测评
//P1307 数字反转
//思路,对0作特判
//将负数转成正数,同时打印负号
//编程经验多了,考虑问题更全面,编码的时候更追求成功率
//翻了一下,之前的代码,发现 写得也挺棒,都提供给大家
//样例通过,提交AC 2018-5-3
#include <stdio.h>
int s[20];
int main(){
int n,len=0,k,i;
scanf("%d",&n);
if(n==0){
printf("0");
return 0;
}
if(n<0){
printf("-");
n=-n;
}
while(n){
s[len++]=n%10;
n/=10;
}
k=0;
while(s[k]==0)k++;
for(i=k;i<len;i++)
printf("%d",s[i]);
return 0;
}
2017-4-6 写得也挺棒
#include <stdio.h>
int main(){
int n;
int ans=0;
scanf("%d",&n);
if(n<0){
printf("-");
n=-n;
}
while(n){
ans*=10;
ans+=n%10;
n/=10;
}
printf("%d\n",ans);
return 0;
}
2.统计单词数
https://www.luogu.org/problemnew/show/P1308可提交测评
// P1308 统计单词数
//对该题,题意不明,感觉抓狂,出题,还是要经得起推敲
//因要读入空格,故采用getchar();
//样例通过,提交80分,测试点9,10 WA
//反复读题,发现 1≤ 文章长度≤1,000,000。 即有可能 文章中最长的字符串 长度为 1000000
//修改,提交AC,
//反馈,该题出得太烂,语意不明,不清之处 至少有两处。
//测试数据中,文章中单词的长度,尽然可以超过20
//一句话,让应试者不在读题上失分,是出题者应注意的。
//提交AC 2018-5-4
//看了此次代码,发现,写得比2017-3-13 还要棒
#include <stdio.h>
#include <string.h>
char a[20],b[1000100];//此处写成 b[20] 又是查数据,又是看代码 ,最后还是读题才解决的
void toLower(char a[]){//字符串转小写
int len,i;
len=strlen(a);
for(i=0;i<len;i++)
if('A'<=a[i]&&a[i]<='Z')
a[i]=a[i]-'A'+'a';
}
int cmp(char a[],char b[]){//相同 返回1 否则返回 0
int len_a,len_b,i;
len_a=strlen(a);
len_b=strlen(b);
if(len_a!=len_b)
return 0;
for(i=0;i<len_a;i++){
if('A'<=b[i]&&b[i]<='Z')
b[i]=b[i]-'A'+'a';
if(a[i]!=b[i])
return 0;
}
return 1;
}
int main(){
char c;
int cnt=0,len=0,pos=-1,num=0;
scanf("%s",a);
toLower(a);
while((c=getchar())!='\n');//吸收 换行 //此处写成while((c=getchar())!='\r');不行
while(c=getchar()){//此处写成 (c=getchar())!=EOF
cnt++;
if(c==' '||c==EOF){
b[len]='\0';
if(len>0)//开始比较
if(cmp(a,b)){
num++;
if(num==1)
pos=cnt-len-1;
}
len=0;
if(c==EOF)break;
}else
b[len++]=c;
}
if(pos==-1)printf("-1");
else printf("%d %d",num,pos);
return 0;
}
//P1308 统计单词数
//觉得该题挺简单的,编码,样例通过,提交,只有20分,通过以下样例
//输入:
//td
// Td tLWCsrmt
//输出:
//1 2
//才明白,以下句子的含义:
//"即在文章中第一次出现时,单词首字母在文章中的位置,位置从 0 开始" ,说实话,题出得不好,理解上很难达成与上述样例一致
//以下为20分代码 2018-5-3 21:24
#include <stdio.h>
#include <string.h>
char a[20],b[20];
int pos=-1,cnt_a=0,cnt_b=0;
int cmp(char a[],char b[]){//不相同,返回0;相同,返回1
int i,len_a,len_b;
len_a=strlen(a);
len_b=strlen(b);
if(len_a!=len_b)
return 0;
for(i=0;i<len_a;i++){
if('A'<=a[i]&&a[i]<='Z')//转换为小写
a[i]=a[i]-'A'+'a';
if('A'<=b[i]&&b[i]<='Z')
b[i]=b[i]-'A'+'a';
if(a[i]!=b[i])
return 0;
}
return 1;
}
int main(){
int i,j;
scanf("%s",a);
while(scanf("%s",b)!=EOF){
cnt_b++;
if(cmp(a,b)){
cnt_a++;
if(cnt_a==1)
pos=cnt_b-1;
}
}
if(pos==-1)
printf("-1");
else
printf("%d %d",cnt_a,pos);
return 0;
}
3.瑞士轮
https://www.luogu.org/problemnew/show/P1309可提交测评
4.表达式的值
https://www.luogu.org/problemnew/show/P1310可提交测评
NOIP 2010 普及组 复赛 试题
1.数字统计 two
2.接水问题 water
3.导弹拦截 missile
4.三国游戏 sanguo
NOIP 2009 普及组 复赛 试题
1.多项式输出 poly
2.分数线划定 score
3.细胞分裂 cell
4.道路游戏 game