tenth!(阶段测试赛)


山峰

Time Limit: 1000MS  Memory Limit: 65536KB
Problem Description

金石山脉有n个山峰,一字排开,从西向东依次编号为1, 2, 3, ……, n。编号为i的山峰高度为hi。每个山峰的高度两两不同

小木示从西向东依次爬过这n个山峰,到每一个山峰的山顶的时候,他都会往西边眺望,并且会记录下自己能看到的山峰的个数。
(比如说小木示 现在在4号山峰,前四号山峰的高度分别为9,4,5,1。他现在能看到的山峰个数就是2,因为第二个山峰被第三个山峰挡住了)

严格的来说,小木示在i位置的时候,对于一个山峰j (j < i),如果不存在一个山峰k满足hj < hk (j < k < i)。则山峰j是可见的。

小木示把自己记录的山峰的个数加和作为这次爬山的快乐值,现在给你n个山峰的高度,求小木示的快乐值。

Input

多组输入,首先输入一个n(1 <= n <= 10^6),表示山峰的个数。

接下来一行n个数,表示对应山峰的山峰的高度(1 <= h <= 10^6)。

Output

输出小木示的快乐值

Example Input
4
1 6 5 1
Example Output
4
利用栈的思想。
数组模拟栈或者栈函数。

  
  
01#include <bits/stdc++.h>
02using namespace std;
03int main()
04{
05long long sum;//数据较大。
06int a,n;
07while(~scanf("%d",&n))
08{
09stack <int> s;
10sum=0;
11while(n--)
12{
13scanf("%d",&a);
14sum+=s.size();
15//每次都加上栈里面元素的数量。
16//因为栈里面的山峰一定是递减的所以
17//所在的山峰前面只要在栈里就全能能看见
18while(!s.empty()&&s.top()<a)
19s.pop();
20//此步骤是维护栈里面的山峰一直是递减的存在
21s.push(a);
22}
23cout<<sum<<endl;
24}
25return 0;
26}
27 
  
  
01#include <bits/stdc++.h>
02using namespace std;
03int main()
04{
05long long sum;//数据较大。
06int a[1000005],n,x,top;
07while(~scanf("%d",&n))
08{
09sum=0;
10top=0;
11while(n--)
12{
13scanf("%d",&x);
14sum+=top;
15//每次都加上栈里面元素的数量。
16//因为栈里面的山峰一定是递减的所以
17//所在的山峰前面只要在栈里就全能能看见
18while(top>0&&a[top]<x)
19top--;
20//此步骤是维护栈里面的山峰一直是递减的存在
21a[++top]=x;
22}
23cout<<sum<<endl;
24}
25return 0;
26}

数据结构实验之求二叉树后序遍历和层次遍历

裸题!熟悉二叉树的各种序列的互推方式。知道前中或前后去建树,然后进行各种遍历,四中遍历方式 要非常熟悉。
Time Limit: 1000MS  Memory Limit: 65536KB
Problem Description

 已知一棵二叉树的前序遍历和中序遍历,求二叉树的后序遍历和层序遍历。

Input

 输入数据有多组,第一行是一个整数t (t<1000),代表有t组测试数据。每组包括两个长度小于50 的字符串,第一个字符串表示二叉树的先序遍历序列,第二个字符串表示二叉树的中序遍历序列。

Output

每组第一行输出二叉树的后序遍历序列,第二行输出二叉树的层次遍历序列。

Example Input
2
abdegcf
dbgeafc
xnliu
lnixu
Example Output
dgebfca
abcdefg
linux
xnuli

  
  
01#include <bits/stdc++.h>
02using namespace std;
03struct node
04{
05    char data;
06    struct node *l;
07    struct node *r;
08};
09struct node *creat(int n,char a[],char b[])
10{
11struct node*root;
12char *p;
13if(n==0)
14return NULL;
15root=new node;
16root->data=a[0];//由于已知先序遍历和中序遍历。
17//可以知道先序遍历的第一个元素就是二叉树根的元素。
18for(p=b;p!='\0';p++)
19//在中序遍历中找到这个二叉树的根
20//左边的递归调用。//右边的为右侧递归调用。
21if(*p==a[0])
22break;
23//找到之后结束
24int t=p-b;//记录下他在数组中的距离
25root->l=creat(t,a+1,b);//左侧递归
26//字符串长度变为T调用从第一个字符串+1
27root->r=creat(n-1-t,a+1+t,p+1);//右侧递归
28return root;
29}
30void houxu(struct node*root)
31{
32    if(root)
33    {
34        houxu(root->l);
35        houxu(root->r);
36        cout<<root->data;
37    }
38}
39//后序遍历
40void cengxu(struct node*root)
41{
42struct node *temp[100];
43int in=0,out=0;
44temp[in++]=root;
45while(in>out)
46{
47if(temp[out])
48{
49cout<<temp[out]->data;
50//输出一次我就让队列++存入输出的那个节点的左右儿子
51temp[in++]=temp[out]->l;
52temp[in++]=temp[out]->r;
53}
54out++;
55//输出一个队列+1
56}
57}//层序遍历利用队列的思想。
58int main()
59{
60int t,len;
61struct node*root=new node;
62char a[55],b[55];//定义两个字符串
63cin>>t;
64while(t--)
65{
66cin>>a>>b;
67len=strlen(a);//求长度。
68root=creat(len,a,b);
69houxu(root);
70cout<<endl;
71cengxu(root);
72cout<<endl;
73}
74return 0;
75}

Old MacDonald Had A Farm

Time Limit: 1000MS  Memory Limit: 65536KB
Problem Description

Old MacDonald Had A Farm, cycyk~

老麦克唐纳德有一个农场,农场里有马、兔、牛三种动物。开始时他有母马、母兔、母牛各 1 只。

对于每头母马,它从第二年起每年年初生一头小母马。每头小母马从第二个年头开始,每年年初也生一头小母马。

对于每只母兔,它从第二年起每年年初生一只小母兔。每只小母兔从第三个年头开始,每年年初也生一只小母兔。

对于每头母牛,它从第二年起每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。

现在老麦克唐纳德想知道,第 n 年时他总共有多少只动物(这里只考虑母马、母兔和母牛)?

Input

输入数据有多组(数据组数不超过 100),到 EOF 结束。

对于每组数据,输入 1 行,包含 1 个整数 n (1 <= n <= 30),表示询问的年数。

Output

对于每组数据,输出 1 行,包含 1 个整数,表示答案。

Example Input
1
2
3
4
Example Output
3
6
10
17
Hint

第 1 年时,老麦克唐纳德有母马 1 头、母兔 1 只、母牛 1 头。

第 2 年时,老麦克唐纳德有母马 2 头、母兔 2 只、母牛 2 头。

第 3 年时,老麦克唐纳德有母马 4 头、母兔 3 只、母牛 3 头。

第 4 年时,老麦克唐纳德有母马 8 头、母兔 5 只、母牛 4 头。



  
  
01#include <bits/stdc++.h>
02using namespace std;
03int a[35]={1,2,4,8},b[35]={1,2,3,5},c[35]={1,2,3,4};
04int main()
05{
06int n,i;
07while(cin>>n)
08{
09if(n>3)
10for(i=4;i<n;i++)
11{
12a[i]=2*a[i-1];
13b[i]=b[i-1]+b[i-2];
14c[i]=c[i-1]+c[i-3];
15}
16cout<<a[n-1]+b[n-1]+c[n-1]<<endl;
17}
18return 0;
19} 要通过给出的数据进行规律分析。

顺序表应用8:最大子段和之动态规划法

Time Limit: 5MS  Memory Limit: 500KB
Problem Description

 给定n(1<=n<=100000)个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n。 例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,最大子段和为20。

 

注意:本题目要求用动态规划法求解,只需要输出最大子段和的值。

Input

第一行输入整数n(1<=n<=100000),表示整数序列中的数据元素个数;

第二行依次输入n个整数,对应顺序表中存放的每个数据元素值。

Output

输出所求的最大子段和

 

Example Input
6
-2 11 -4 13 -5 -2
Example Output
20

  
  
01#include <stdio.h>
02using namespace std;
03int a[100005];
04int main()
05{
06int n,i,m=0,sum=0;
07scanf("%d",&n);
08for(i=0;i<n;i++)
09{
10scanf("%d",&a[i]);
11if(a[i]+m>0)
12m+=a[i];
13else
14m=0;
15if(m>sum)
16sum=m;
17}
18printf("%d\n",sum);
19return 0;
20}

这道题是典型的求最大连续子序列和的问题。我们首先回顾一下动态规划法求最大连续子序列和的方法:

int max_sum = 0, this_sum = 0;
for(int i=0; i<n; ++i) {
    this_sum += a[i]; //向右累加
    if(this_sum > max_sum) //更新最大子序列和
        max_sum = this_sum;
    if(this_sum < 0) //如果当前子序列和为负
        this_sum = 0; //则抛弃当前子序列
}
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

参照以上代码,我们举一个例子来说明: 
对于序列 3 -2 4 -6 5 1, 
遍历到 3 时:当前子序列为 3,this_sum = 3,max_sum = 3; 
遍历到 -2 时:当前子序列为 3 -2,this_sum = 1,max_sum = 3; 
遍历到 4 时:当前子序列为 3 -2 4,this_sum = 5,max_sum = 5; 
遍历到 -6 时:当前子序列为 3 -2 4 -6,this_sum = -1,此时当前子序列和为负,如果不抛弃当前子序列而继续累加的话,下次的子序列和等于下一个元素加上一个负数,即 -1 + 5 = 4 < 5,必然比直接从下一个元素开始重新计算的子序列和要小,故抛弃当前子序列和,从下一个元素开始重新计算子序列和。this_sum = 0,max_sum = 5; 
遍历到 5 时:当前子序列为 5,this_sum = 5,max_sum = 5; 
遍历到 1 时:当前子序列为 5 1,this_sum = 6,max_sum = 6; 
故得到结果为 6。

回到硬币问题上来,我们可以把翻硬币的过程相对初始状态比较,反面翻到正面就是正面的个数 +1,而正面翻到反面就是正面的个数 -1,因此我们可以把这个相对状态看作是一个由 1 和 -1 组成的序列,按照上面的方法求解即可。

这样我们就可以写出本题的代码了:

#include <cstdio>
using namespace std;

int main(int argc, char const *argv[]) {
    int n, a[1000];
    while(~ scanf("%d", &n)) {
        //sum 为初始状态的正面向上的硬币数
        int sum = 0;
        for(int i=0; i<n; ++i) {
            scanf("%d", &a[i]);
            if(a[i] == 1) sum++;
        }
        int max_sum = 0, this_sum = 0;
        for(int i=0; i<n; ++i) {
            if(a[i] == 0) //反面翻到正面看作 1
                this_sum++;
            else //正面翻到反面看作 -1
                this_sum--;
            if(this_sum > max_sum)
                max_sum = this_sum;
            if(this_sum < 0)
                this_sum = 0;
        }
        printf("%d\n", sum + max_sum);
    }

    return 0;
}



动态规划?
Time Limit: 1000ms Memory limit: 65536K

题目描述
动态规划作为《运筹学》的一个分支,被广泛的用于解决较为复杂的经济管理问题,以达到的最优抉择,获得最大经济收益为目的。也因其多变性,非常的频繁的出现在信息学竞赛的赛场上。
动态规划的核心思想为不断将问题分解为子问题,一直到可以较容易的得到最优答案,再去决定其父问题的决策,因为很大程度的避免了重复子问题的抉择,故可以节约大量时间。

现在问题来了,有一个一维数组,存储了n个正整数,下标依次为0,1,2,….,n-1。
现在要从中选取一部分数,你要给出一个选择方案使得你的方案满足下列要求:
1. 这部分元素的下标应满足st,st+5, st+5*2 , st+5*3, … , st+5*x (0 <= st < n ,st <= st+5*x < n)。
2. 在满足第一条要求的方案中,应选取其累加和最大的一种的方案。

输入
多组输入。
对于每组输入:
第一行输入一个n(1 <= n <= 100000)。
接下来的一行有n个整数y(-100000 <= y <= 100000)。

输出
对于每组数据,输出一个整数代表你的方案的累加和。

示例输入
10
1 2 3 4 5 6 7 8 9 10
3
1 -10 2
3
-1 -2 -3

示例输出
15
2
-1

提示

来源
zmx

解题思路

这道题是典型的求最大连续子序列和的问题。其求法可以参考我的这篇文章

理解了最大连续子序列和的算法,这道题就不难解决了。由于每种方案的元素下标都满足 st + 5*n,因此我们可以把 st 从 0 到 4 的一共 5 种方案看作 5 个可找到的最长序列,对每个序列求其最大连续子序列和,最后再从这 5 个最大连续子序列和中找出最大的即得到结果。

这样我们就可以写出本题的代码了:

#include <cstdio>
using namespace std;

int n, a[100000];

int Solve(int st);

int main(int argc, char const *argv[]) {
    while(~ scanf("%d", &n)) {
        for(int i=0; i<n; ++i) {
            scanf("%d", &a[i]);
        }
        int max_sum = Solve(0), sum;
        //遍历所有 st,并且应注意 n < 5 的情况
        for(int i=1; i<5 && i<n; ++i) {
            sum = Solve(i);
            if(sum > max_sum)
                max_sum = sum;
        }
        printf("%d\n", max_sum);
    }

    return 0;
}

//求最大连续子序列和
int Solve(int st) {
    int max_sum = -100000, this_sum = 0;
    for(int i=st; i<n; i+=5) {
        this_sum += a[i];
        if(this_sum > max_sum)
            max_sum = this_sum;
        if(this_sum < 0)
            this_sum = 0;
    }

    return max_sum;
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38


巨斧砍大树
Time Limit: 1000 Memory Limit: 65536
Description
阿福最近练就了一个新的招式:巨斧砍大树。这个招式可以砍掉一颗二叉搜索树的某个子树。现在,阿福面前有一颗 n 个结点的二叉搜索树,他要使用 m 次招式,于是他想询问你每次使用「巨斧砍大树」后二叉搜索树会被砍成什么样子。


二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉搜索树。


Input
第 1 行输入 2 个整数 n, m (1 <= n, m <= 10)。表示二叉搜索树的结点个数和招式使用次数。


第 2 行输入 n 个空格隔开的整数 v (1 <= v <= 10),表示二叉搜索树是以此序列顺序插入生成的二叉树(保证互不相同)。


接下来输入 m 行,每行一个整数 w (1 <= w <= 10),表示阿福要砍掉结点上数值为 w 的子树(保证 w 是初始二叉树上存在的数值)。


Output
对于每次砍树,如果成功砍掉子树,则先输出一行 "Cut x",其中 x 为被砍掉子树的根节点上的数值。如果要砍掉的结点在之前已被砍掉,则输出一行 "Already cut x",x 的含义同上。


随后输出一行,表示此次砍树结束后当前二叉树的中序遍历结果,以空格分隔(行末没有多余空格,如果整颗二叉树已为空,则输出一行空行)。


Sample Input
5 5
1 3 2 4 5
5
2
3
4
1


Sample Output
Cut 5
1 2 3 4
Cut 2
1 3 4
Cut 3
1
Already cut 4
1

Cut 1

代码:

#include <stdio.h>
#include <stdlib.h>
/*
1、本题思想其实很简单,只需要建立一个排序二叉树,然后查找数据元素
所在二叉树的位置,找到以后将其根节点变为空,同时将其左右子树变为空
2、本题输出遍历后的数据元素时,因为有具体要求(最后一个元素后面无空格)
所以我们可以使用一个数组来保存元素*/
typedef int element ;
typedef struct node
{
    element data;
    struct node *lchild,*rchild;
} Bnode,*BiTree;
int a[100];
int k;int g;
//建立排序二叉树
BiTree creat(BiTree root,int k)
{
    if(!root)
    {
        root=(BiTree )malloc(sizeof(Bnode));
        root->data=k;
        root->lchild=NULL;
        root->rchild=NULL;
    }
    else
    {
        if(k>root->data)
            root->rchild=creat(root->rchild,k);
        if(k<root->data)
            root->lchild=creat(root->lchild,k);
    }
    return root;
}
//在二叉树中查找到具体元素,并将其和左右子树变为空
BiTree find(BiTree root,int k)
{
    if(root)
    {
        if(root->data==k)  //找到数据元素,并将标记记为1;
        {
            root->lchild=NULL;
            root->rchild=NULL;
            root=NULL;
            g=1;
        }
        else  //左右寻找数据元素
        {
            if(k<root->data)
                root->lchild=find(root->lchild,k);
            if(k>root->data)
                root->rchild=find(root->rchild,k);
        }
    }
    else   //找不到数据元素,标记为0;
    {
        g=0;
    }
    return root;
}
void midprint(BiTree root)  //输出中序遍历结果
{
    if(root)
    {
        midprint(root->lchild);
        a[k++]=root->data;
        midprint(root->rchild);
    }
}
int main()
{
    BiTree root=NULL;
    int n,m;
    int i;
    int t;int p;
    scanf("%d %d",&n,&m);
    for(i=0; i<n; i++)
    {
        scanf("%d",&t);
        root=creat(root,t);
    }
    while(m--)
    {
        k=0;
        scanf("%d",&p);
        root=find(root,p);
        if(g==0)  //当标记为0时,说明以此元素为根节点的值为空,即该元素已被砍掉
            printf("Already cut %d\n",p);
        if(g==1)   //当标记为1时,说明该元素还为被删除
        {
            printf("Cut %d\n",p);
            midprint(root);
            if(k==0)  //k为0,说明整个二叉树为空,无数据元素
                printf("\n");
            else  //k不为0,输出数组元素
            {
                for(i=0;i<k-1;i++)
                    printf("%d ",a[i]);
                printf("%d\n",a[k-1]);


            }
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值