总目录详见https://blog.csdn.net/mrcrack/article/details/84471041
做题原则,找不到测评地址的题不做。2018-11-28
重走长征路---OI每周刷题记录---10月20日 2013
本周共计10题
测评地址:
卡特兰数
1.栈 //在线测评地址https://www.luogu.org/problemnew/show/P1044
树
2.单词查找树 //在线测评地址http://codevs.cn/problem/1729/
floyd
3.医院设置 //在线测评地址https://www.luogu.org/problemnew/show/P1364
模拟
4.破碎的项链 //在线测评地址https://www.luogu.org/problemnew/show/P1203
5.奖学金 //在线测评地址https://www.luogu.org/problemnew/show/P1093
暴力
6.火柴棒等式 //在线测评地址https://www.luogu.org/problemnew/show/P1149
dp
7.导弹问题2 //偶然找到在线测评地址http://218.5.5.242:9018/JudgeOnline/problem.php?id=1275
8.拔河比赛 //在线测评地址http://codevs.cn/problem/1959/
9.最大连续子串和 //在线测评地址https://www.luogu.org/problemnew/show/P1115
10.最长公共子序列 //在线测评地址http://ybt.ssoier.cn:8088/problem_show.php?pid=1265
题解:
卡特兰数
1.栈
//P1044 栈
//在线测评地址https://www.luogu.org/problemnew/show/P1044
//模拟发现
//f(1)=1,f(2)=f(1)*f(1)+1=2
//f(3)=f(1)*f(2)+f(2)*f(1)+1=5
//猜测f(n)=f(1)*f(n-1)+f(n-1)*f(1)+f(2)*f(n-2)+f(n-2)*f(2)+...
//有一个担忧,int是否够用,编好后,测试了再说
//测试发现n=18,溢出,果断改成long long
//样例通过,提交,测试点3-5WA,还算满意,总归是模拟出了问题
//此文思路不错https://www.luogu.org/problemnew/solution/P1044?page=3摘抄如下
//假设k是最后一个出栈的数。那么比k早进栈且早出栈的有k-1个数,一共有h(k-1)种方案。比k晚进栈且早出栈的有n-k个数,一共有h(n-k)种方案。所以一共有h(k-1)*h(n-k)种方案。显而易见,k取不同值时,产生的出栈序列是相互独立的,所以结果可以累加。k的取值范围为1至n,所以结果就为h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0)。
//修改,样例通过,提交AC。2019-1-7 21:14
#include <stdio.h>
#include <string.h>
long long f[20];
int n;
int main(){
int i,j;
memset(f,0,sizeof(f));
scanf("%d",&n);
f[1]=f[0]=1;
for(i=2;i<=n;i++){
for(j=1;j<=i;j++)
f[i]+=f[j-1]*f[i-j];
}
printf("%lld\n",f[n]);
return 0;
}
树
2.单词查找树
//1729 单词查找树
//2000 NOI
//在线测评地址http://codevs.cn/problem/1729/
//若题中能给出样例的解释最好不过了。
//查找https://blog.csdn.net/u011815404/article/details/80637388找到下图
//该题若是读者第一次面对,建议给出上图,便于理解题意。
//有了上图后,几个疑问基本解决,单词一定能在图中找到
//图中找到的词未必出现在单词中。
//题意清楚后,
//32K按32*1024=32768计算,最多有32768个节点
//研究上图,可以很明显的发现,root暂且称为第0层,单词的第一个字母出现在第一层
//第二个字母出现在第二层,第一个字母相同的,第二个字母也相同的,可以占用第一层,第二层的同一个节点
//纯模拟编写即可,先将单词按字典序排序
//再统计每层不同的字母,累加,即可。
//字符串的sort写法第一词用,边写边测试吧
//C++是免不了了
//发现不会用,那么转回 手写的 快速排序,
//等程序编写AC后,再回头学习sort针对字符串的写法
//快排编好后,测试成功,放心的进入下一步。
//一直觉得自身算法难以突破,扫了一遍https://blog.csdn.net/u011815404/article/details/80637388代码
//直呼想不到,最核心的一句 累加两个单词的差
//样例通过,提交WA,输出结果一看,发现有一处测试语句未删除。遗憾
//修改,提交AC。2019-1-5 21:07
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int len[32800];//每个字符串的长度
char s[32800][65];
void quick_sort(int left,int right){//字典序,自小到大
int i=left,j=right;
char mid[65],t[65];
strcpy(mid,s[(left+right)/2]);
while(i<=j){
while(strcmp(s[i],mid)<0)i++;
while(strcmp(mid,s[j])<0)j--;
if(i<=j){
strcpy(t,s[i]);
strcpy(s[i],s[j]);
strcpy(s[j],t);
i++,j--;
}
}
if(i<right)quick_sort(i,right);
if(left<j)quick_sort(left,j);
}
int main(){
int i,j,k=0,cnt;
while(scanf("%s",s[k])!=EOF)len[k]=strlen(s[k]),k++;//此处写成len[k]=strlen(s[k]),太急了,长度的计算应该发生在排序后//读入数据
quick_sort(0,k-1);//0-k-1
for(i=0;i<k;i++)len[i]=strlen(s[i]);
cnt=len[0];
for(i=1;i<k;i++){
j=0;
while(j<len[i-1]&&j<len[i]&&s[i-1][j]==s[i][j])j++;//s[i-1]与s[i]串雷同部分
cnt+=len[i]-j;//s[i]串独有的部分len[i]-j;
}
cnt+=1;//加上root节点
printf("%d\n",cnt);
return 0;
}
floyd
3.医院设置
//P1364 医院设置
//在线测评地址https://www.luogu.org/problemnew/show/P1364
//没什么好想法,纯模拟
//一个点一个点的开始遍历,
//采用邻接矩阵的方式存储 二叉树
//转念一想,上述存储方式,算法复杂度太高
//还是如下存储a[i][0] 节点i的左右子数个数。
//a[i][1]节点i的第一个子树根节点,a[i][2]节点i的第二个子树根节点
//权衡了一下,感觉遍历采用深搜dfs,编起来更方便
//题目需补充,居民数的范围,该题,无法测算距离和,无奈,只能让最大值为999999999
//样例通过,提交AC。2019-1-4
//高兴,明显感觉水平上升,尤其深搜水平。
#include <stdio.h>
#include <string.h>
#define maxn 110
int n,a[maxn][5],w[maxn],vis[maxn],ans;//w[i]节点i的人口数,也即权重。
int min(int a,int b){
return a<b?a:b;
}
void dfs(int x,int step){
int i,v;
for(i=1;i<=a[x][0];i++){
v=a[x][i];
if(vis[v]==0){
vis[v]=1;
ans+=w[v]*step;
dfs(v,step+1);
}
}
}
int main(){
int i,left,right,d=999999999;//此处写成d=-1
memset(a,0,sizeof(a));
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d%d%d",&w[i],&left,&right);
if(left!=0)
a[i][++a[i][0]]=left,a[left][++a[left][0]]=i;//i与left互为子树
if(right!=0)
a[i][++a[i][0]]=right,a[right][++a[right][0]]=i;
}
for(i=1;i<=n;i++){
memset(vis,0,sizeof(vis)),vis[i]=1,ans=0;
dfs(i,1);
d=min(d,ans);
}
printf("%d\n",d);
return 0;
}
//P1364 医院设置
//在线测评地址https://www.luogu.org/problemnew/show/P1364
//一直没反应过来,该题可以用Floyd算法,
//先采用深搜dfs编写AC。
//看了https://blog.csdn.net/yanyanwenmeng/article/details/77712852代码,开始动手Floyd算法
//样例通过,提交AC。2019-1-4
#include <stdio.h>
#define INF 999999999
#define maxn 110
int n,a[maxn][maxn],w[maxn];
int min(int a,int b){
return a<b?a:b;
}
int main(){
int i,j,k,u,v,ans=INF,total;
scanf("%d",&n);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(i==j)a[i][j]=0;
else a[i][j]=INF;
for(i=1;i<=n;i++){
scanf("%d%d%d",&w[i],&u,&v);
a[i][u]=a[i][v]=a[u][i]=a[v][i]=1;
}
for(k=1;k<=n;k++)//Floy算法
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
for(i=1;i<=n;i++){
total=0;
for(j=1;j<=n;j++)
total+=a[i][j]*w[j];
ans=min(ans,total);
}
printf("%d\n",ans);
return 0;
}
模拟
4.破碎的项链
//P1203 [USACO1.1]坏掉的项链Broken Necklace
//在线测评地址https://www.luogu.org/problemnew/show/P1203
//该题纯枚举,即可做出
//三串项链串成数组,从中间一串开始进行枚举,向左,向右,取最大值
//有一个需要特判,若统计的数目大于珠子个数,那么,值只能为N
//样例通过,提交,测试点2,4 WA,78分。2019-1-2 18:09
//翻看了测试点2的数据https://www.luogu.org/discuss/show/19648
//3 rrr
//修改,if(ans>=n){//添加此判断功能
//提交AC,此题的问题,编写前,特判考虑到了,但程序没编好,造成了 测试点2,4 WA
//还是要靠 测试数据 来 考验 代码。2019-1-2 19:58
//还是比较高兴的,一串变三串的处理方式,独立想出。
//翻看了以前编写的代码,写得真是够差劲。2019-1-2 20:04
#include <stdio.h>
#include <string.h>
int n;
char s[1100],c;
int max(int a,int b){
return a>b?a:b;
}
int main(){
int i,j,cnt,ans=-1;
scanf("%d%s",&n,s+1);
for(i=1;i<=n;i++)
s[i+2*n]=s[i+n]=s[i];//三串项链
for(i=n+1;i<=2*n;i++){//中间串项链开始遍历
cnt=0,j=i;//左扫
while(j>=i-n+1&&s[j]=='w')j--,cnt++;
c=s[j];//非'w'字母
while(j>=i-n+1&&(s[j]=='w'||c==s[j]))j--,cnt++;
j=i+1;//右扫
while(j<=i+n-1&&s[j]=='w')j++,cnt++;
c=s[j];
while(j<=i+n-1&&(s[j]=='w'||c==s[j]))j++,cnt++;
ans=max(ans,cnt);
if(ans>=n){//添加此判断功能
ans=n;
break;
}
}
printf("%d\n",ans);
return 0;
}
5.奖学金
//P1093 奖学金
//NOIP 2007 普及组
//在线测评地址https://www.luogu.org/problemnew/show/P1093
//n(≤300)冒泡排序即可处理
//样例通过,提交AC。2019-1-2
#include <stdio.h>
struct node{
int i,yu,shu,ying,zong;
}stu[305],stu_t;
void swap(int i,int j){
stu_t=stu[i],stu[i]=stu[j],stu[j]=stu_t;
}
int main(){
int n,i,j;
scanf("%d",&n);
for(i=1;i<=n;i++){
stu[i].i=i;
scanf("%d%d%d",&stu[i].yu,&stu[i].shu,&stu[i].ying);
stu[i].zong=stu[i].yu+stu[i].shu+stu[i].ying;
}
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
if(stu[i].zong<stu[j].zong)
swap(i,j);
else if(stu[i].zong==stu[j].zong){//此处写成else if(stu[i].zong=stu[j].zong)有失水准
if(stu[i].yu<stu[j].yu)
swap(i,j);
else if(stu[i].yu==stu[j].yu){
if(stu[i].i>stu[j].i)
swap(i,j);
}
}
for(i=1;i<=5;i++)
printf("%d %d\n",stu[i].i,stu[i].zong);
return 0;
}
暴力
6.火柴棒等式
//P1149 火柴棒等式
//NOIP 2008 提高组
//在线测评地址https://www.luogu.org/problemnew/show/P1149
//开一个数组,记录数字所用火柴棒数量
//24-4=20 20/2=10 最多10个1
//猜测数据不会很大,最多3位数+3位数=3位数
//需编写将多位数解析为一个一个数的函数
//100*100 对应 输入24 答案为100
//1000*1000 对应 输入24 答案为128
//3000*3000 对应 输入24 答案为128
//不放心,在3000*3000范畴内,将n<=24测了一遍
//之后放心的改回1000*1000
//提交AC。2019-1-2 21:02
//翻了之前的代码,发现写法,范围,基本雷同。
//有一个收获,火柴棒根数 与 等式数量 并不是单调函数
#include <stdio.h>
int a[]={6,2,5,5,4,5,6,3,7,6};
int count(int x){
int ans=0;
if(x==0)return a[0];//特判
while(x){
ans+=a[x%10];
x/=10;
}
return ans;
}
int main(){
int i,j,n,cnt=0;
scanf("%d",&n);
n-=4;//扣除 等号 加号
for(i=0;i<=1000;i++)
for(j=0;j<=1000;j++)
if(n==count(i)+count(j)+count(i+j))
cnt++;
printf("%d\n",cnt);
}
dp
7.导弹问题2
导弹问题2
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000 的正整数),该导弹数量不超过100个,计算这套系统最多能拦截多少导弹,分别是哪些导弹,本题保证所给的数据最优解只有一组
样例:
INPUT
389 207 155 300 299 170 158 65
OUTPUT
6(最多能拦截的导弹数)
389 300 299 170 158 65
样例输入
389 207 155 300 299 170 158 65
样例输出
6
389 300 299 170 158 65
//导弹问题2
//找不到测评地址,不过,觉得该题,有锻炼价值,
//该题正好处于 本人 的非舒适区,决定 刻意训练
//故决定还是编写。2019-1-7 17:26
//相信一定能找到该题的在线测评地址,也欢迎读者提供。
//偶然找到在线测评地址http://218.5.5.242:9018/JudgeOnline/problem.php?id=1275
//提交,AC。高兴,夙愿解决。2019-1-14 8:51
#include <stdio.h>
#include <string.h>
#define maxn 110
int a[maxn],d[maxn],pre[maxn];
void print(int x){//打印选择的导弹
if(x==-1)return;//此处写成 if(x=-1)return; 昏招
print(pre[x]);
printf("%d ",a[x]);
}
int main(){
int i,j,n=0,ans=-1,k;
memset(pre,-1,sizeof(pre));
while(scanf("%d",&a[n])!=EOF)n++;
pre[0]=-1;
for(i=0;i<n;i++){
d[i]=1;
for(j=0;j<i;j++)//下降序列
if(a[j]>=a[i]&&d[j]+1>d[i]){
d[i]=d[j]+1,pre[i]=j;
if(ans<d[i])
ans=d[i],k=i;
}
}
printf("%d\n",ans);
print(k);
return 0;
}
8.拔河比赛
//1959 拔河比赛
//在线测评地址http://codevs.cn/problem/1959/
//一点感觉都没有,唯一能做的就是 和/2,输出,骗个10分
//试了试,果真骗到10分,测试点2 通过。
//正常的算法,猜测,排序肯定少不了。
//接下来怎么样,想不到了。
//看了题解,01背包,f[110][55][23000] 110*55*23000*4/1024/1024=531M 爆内存无疑
//需用 降维 的 01背包,f[i][j][k] 前i个人,挑j个,对应的质量k.布尔背包,采用|的关系
//逆序,j,k均需逆序。j=min(i,m)
//此文,思路,代码都写得不错。https://blog.csdn.net/qq_34374664/article/details/70339398
//样例通过,提交,测试点3-6,8-9 WA。2019-1-14 14:55
//还需判定最后输出的大小关系,修改,提交,测试点4-6,9 WA。2019-1-14 14:59
//for(k=d;k>=w[i];k--)//此处写成 for(k=d;k>=1;k--)
//修改,提交,测试点5-6WA。2019-1-14 15:10
//f[1][0]=f[0][0]=1;//此处写成f[0][0]=1;
//修改,提交AC。2019-1-14 15:27
//n分奇偶讨论,相交于上述AC情况,更好理解,修改,提交AC。2019-1-14 16:27
#include <stdio.h>
#include <string.h>
#define maxn 110
int w[maxn],n,sum=0,f[55][23000];
int min(int a,int b){
return a<b?a:b;
}
int main(){
int i,j,k,m,p,d;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&w[i]);
sum+=w[i];
}
memset(f,0,sizeof(f));
f[0][0]=1;
d=(sum+1)/2,m=(n+1)/2;//sum,n为奇数处理
for(i=1;i<=n;i++)
for(j=min(i,m);j>=1;j--)
for(k=d;k>=w[i];k--)//此处写成 for(k=d;k>=1;k--)
if(f[j-1][k-w[i]])
f[j][k]=1;
if(n%2==0)p=m;//偶数
else p=m-1;//奇数
for(i=d;i>=1;i--)
if(f[m][i]||f[p][i])//主要在n为奇数时,如n=7,讨论4,3的情况
break;
if(i<sum-i)printf("%d %d\n",i,sum-i);
else printf("%d %d\n",sum-i,i);
return 0;
}
9.最大连续子串和
//P1115 最大子段和
//在线测评地址https://www.luogu.org/problemnew/show/P1115
//sum[i]以a[i]为结尾的最大子段和
//数据最大之10000*200000=2*10^9 int不会溢出
//算法时间复杂度O(n). 样例通过,提交,测试点2 WA。
//翻了一下讨论,发现全是负数,需要特判
//提供一组测试数据
输入
3
-5 -1 -2 -3
输出
-1
//该代码,独立思考,编出,自觉不错。2019-1-8
#include <stdio.h>
#define maxn 200100
int n,a[maxn],sum[maxn];
int max(int a,int b){
return a>b?a:b;
}
int main(){
int i,ans=-999999999;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
sum[0]=0;
for(i=1;i<=n;i++){
sum[i]=a[i];
if(sum[i-1]>=0)sum[i]+=sum[i-1];//此处写成 if(sum[i-1]>=0)sum[i]+=sum[i-1],ans=max(ans,sum[i]);过不了测试点2
}
for(i=1;i<=n;i++)ans=max(ans,sum[i]);
printf("%d\n",ans);
return 0;
}
10.最长公共子序列
//1265:【例9.9】最长公共子序列
//在线测评地址http://ybt.ssoier.cn:8088/problem_show.php?pid=1265
//样例通过,提交AC,不敢相信,乱搞一气也能AC。边看运行中的数据,边编写。2019-1-8
//将混乱的思路进行修改,再次提交,还是AC。
#include <stdio.h>
#include <string.h>
#define maxn 1100
char x[1100],y[1100];
int f[maxn][maxn];
int max(int a,int b){
return a>b?a:b;
}
int main(){
int len_x,len_y,i,j;
memset(f,0,sizeof(f));
scanf("%s%s",x+1,y+1);
len_x=strlen(x+1),len_y=strlen(y+1);
for(i=1;i<=len_x;i++)
for(j=1;j<=len_y;j++){
if(x[i]==y[j])
f[i][j]=f[i-1][j-1]+1;
else
f[i][j]=max(f[i-1][j],f[i][j-1]);//此句写成 f[i][j]=max(f[i-1][j],max(f[i][j-1],f[i-1][j-1]));虽能AC,但感觉比较混乱,还是要修改
}
printf("%d\n",f[len_x][len_y]);
return 0;
}
2019-1-14 16:29 AC 该周内容。