重走长征路---OI每周刷题记录---10月20日 2013 AC 10题

总目录详见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 该周内容。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值