zoukankan      html  css  js  c++  java
  • 『NOIP2018普及组题解』

    <更新提示>

    <第一次更新>


    <正文>

    标题统计

    题目描述

    凯凯刚写了一篇美妙的作文,请问这篇作文的标题中有多少个字符? 注意:标题中可能包含大、小写英文字母、数字字符、空格和换行符。统计标题字 符数时,空格和换行符不计算在内。

    输入格式

    输入文件只有一行,一个字符串 s。

    输出格式

    输出文件只有一行,包含一个整数,即作文标题的字符数(不含空格和换行符)。

    样例数据

    input1

    234
    

    output1

    3
    

    input2

    Ca 45
    

    output2

    4
    

    样例说明

    样例 1 :标题中共有 3 个字符,这 3 个字符都是数字字符。

    样例 2 :标题中共有 5 个字符,包括 1 个大写英文字母, 1 个小写英文字母和 2 个数字字符, 还有 1 个空格。由于空格不计入结果中,故标题的有效字符数为 4 个。

    数据规模与约定

    规定∣s∣ 表示字符串 s 的长度(即字符串中的字符和空格数)。

    对于 %40 的数据,1 ≤ |s| ≤ 5,保证输入为数字字符及行末换行符。

    对于 %80 的数据,1 ≤ |s| ≤ 5,输入只可能包含大、小写英文字母、数字字符及行末换行符。

    对于 %100 的数据,1 ≤ |s| ≤ 5,输入可能包含大、小写英文字母、数字字符、空格和行末换行符。

    时间限制:1s

    空间限制:256MB

    解析

    T1和往年一样,还是签到题。不过这一次好像更注重考察语言了,不少不熟悉语言的小伙伴可能就没有分了啦。
    大概是考察如何处理输入吧,会用(getline())的基本都用了(getline())了吧,当然,不会用的还有其他的办法,主要是(while(cin>>str))(scanf())读到换行符为止。这样的话,只要暴力统计一下就可以了啦。

    (Code:)

    #include<bits/stdc++.h>
    using namespace std;
    string s;int ans=0;
    int main(void)
    {
    	freopen("title.in","r",stdin);
    	freopen("title.out","w",stdout);
    	getline(cin,s);
    	for(int i=0;i<s.size();i++)
    	{
    		if(s[i]!=' ')ans++;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
    
    #include<bits/stdc++.h>
    using namespace std;
    char s;
    int ans=0;
    int main()
    {
    	freopen("title.in","r",stdin);
    	freopen("title.out","w",stdout);
    	while (cin>>s)
    	{
    		if(s>='0'&&s<='9'||s>='a'&&s<='z'||s>='A'&&s<='Z')ans++;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
    

    龙虎斗

    题目描述

    轩轩和凯凯正在玩一款叫《龙虎斗》的游戏,游戏的棋盘是一条线段,线段上有 n个兵营(自左至右编号 1 ∼n),相邻编号的兵营之间相隔 1 厘米,即棋盘为长度为 n-1 厘米的线段。i 号兵营里有 ci位工兵。

    下面图 1 为 n=6的示例:

    轩轩在左侧,代表“龙”;凯凯在右侧,代表“虎”。 他们以 m 号兵营作为分界, 靠左的工兵属于龙势力,靠右的工兵属于虎势力,而第 m号兵营中的工兵很纠结,他们不属于任何一方。

    一个兵营的气势为:该兵营中的工兵数 × 该兵营到 m 号兵营的距离;参与游戏 一方的势力定义为:属于这一方所有兵营的气势之和。

    下面图 2 为 n = 6,m = 4 的示例,其中红色为龙方,黄色为虎方:

    游戏过程中,某一刻天降神兵,共有 s1位工兵突然出现在了 p1号兵营。作为轩轩和凯凯的朋友,你知道如果龙虎双方气势差距太悬殊,轩轩和凯凯就不愿意继续玩下去了。为了让游戏继续,你需要选择一个兵营 p2,并将你手里的 s2位工兵全部派往 兵营 p2,使得双方气势差距尽可能小。

    注意:你手中的工兵落在哪个兵营,就和该兵营中其他工兵有相同的势力归属(如果落在 m 号兵营,则不属于任何势力)。

    输入格式

    输入文件的第一行包含一个正整数n,代表兵营的数量。

    接下来的一行包含 n 个正整数,相邻两数之间以一个空格分隔,第 i个正整数代 表编号为 i 的兵营中起始时的工兵数量 ci。

    接下来的一行包含四个正整数,相邻两数间以一个空格分隔,分别代表 m,p1,s1,s2。

    输出格式

    输出文件有一行,包含一个正整数,即 p2,表示你选择的兵营编号。如果存在多个编号同时满足最优,取最小的编号。

    样例数据

    input1

    6 
    2 3 2 3 2 3 
    4 6 5 2
    

    output1

    2
    

    input2

    6 
    1 1 1 1 1 16 
    5 4 1 1
    

    output2

    1
    

    样例说明

    样例1:见问题描述中的图 2。

    双方以 m=4号兵营分界,有 s1=5位工兵突然出现在p1=6号兵营。 龙方的气势为:

    2×(4−1)+3×(4−2)+2×(4−3)=14

    虎方的气势为:

    2×(5−4)+(3+5)×(6−4)=18

    当你将手中的 s2=2s2=2位工兵派往 p2=2号兵营时,龙方的气势变为:

    14+2×(4−2)=18

    此时双方气势相等。

    样例2:

    双方以 m = 5号兵营分界,有 s1=1位工兵突然出现在 p1=4号兵营。

    龙方的气势为:

    1×(5−1)+1×(5−2)+1×(5−3)+(1+1)×(5−4)=11

    虎方的气势为:

    16×(6−5)=16

    当你将手中的 s2=1位工兵派往 p2=1号兵营时,龙方的气势变为:

    11+1×(5−1)=15

    此时可以使双方气势的差距最小。

    数据规模与约定

    1<m<n,1≤p1≤n。

    对于%20 的数据,(n=3,m=2,ci=1,s1,s2≤100。)

    另有%20 的数据,(n≤10,p1=m,ci=1,s1,s2≤100。)

    对于%60 的数据,(n≤100,ci=1,s1,s2≤100。)

    对于%80 的数据,(n≤100,ci,s1,s2≤100。)

    对于%100 的数据,(n≤10^5,ci,s1,s2≤10^9。)

    时间限制:1s

    空间限制:256MB

    解析

    今年的第二题没有往年怎么简单啦。这回T2虽然说也没有考算法,但是细节问题就很多了,于是就有了不少坑点:

    1.爆int
    2.min的初值需要赋到MAX_LONGLONG大小
    3.需要注意把工兵放在m号兵营的情况

    那么大体思路是这样的,我们暴力枚举把(s2)个工兵放在每一个兵营的情况,看看放在哪里呢得到最优解,直接输出即可。那么,这里就有一个显而易见的优化了:那一边初始的气势值总和最小,那就在那一边枚举。但是,这就如3.,无论是那一边,我们都要判断是否把(s2)个工兵放在(m)号兵营会取得更优的答案,因为存在如下一种情况:

    一开始双方势力值总和的差较小,我们在势力值总和较小的一边枚举,但是(s2)的值巨大,无论放在哪里,新势力值的差比原势力值的差还大,此时,不如把工兵放在(m)号兵营处,让势力值总和不变化

    如果这些都注意到了,那么这题就解决了。

    (Code:)

    #include<bits/stdc++.h>
    using namespace std;
    inline void read(long long &k)
    {
    	long long x=0,w=0;char ch;
    	while(!isdigit(ch))w|=ch=='-',ch=getchar();
    	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    	k=(w?-x:x);return;
    }
    const int Maxn=100000+80;
    long long n,c[Maxn],m,p1,s1,s2,vleft=0,vright=0,Mindif=0x3f3f3f3f,ans;
    inline void input(void)
    {
    	read(n);
    	for(int i=1;i<=n;i++)read(c[i]);
    	read(m),read(p1);
    	read(s1),read(s2);
    	c[p1]+=s1;
    }
    inline void init(void)
    {
    	for(int i=1;i<m;i++)vleft+=(c[i]*(m-i));
    	for(int i=m+1;i<=n;i++)vright+=(c[i]*(i-m));
    }
    inline void work(void)
    {
    	if(vleft<vright)
    	{
    		for(int i=1;i<=m;i++)
    		{
    			long long l=vleft+s2*(m-i);
    			long long dif=abs(vright-l);
    			if(dif<Mindif)
    			{
    				Mindif=dif;
    				ans=i;
    			}
    		}
    	}
    	else if(vright<vleft)
    	{
    		for(int i=m;i<=n;i++)
    		{
    			long long r=vright+(i-m)*s2;
    			long long dif=abs(r-vleft);
    			if(dif<Mindif){
    				
    				Mindif=dif;
    				ans=i;
    			}
    		}
    	}
    	else
    	{
    		ans=m;
    	}
    }
    int main(void)
    {
    	freopen("fight.in","r",stdin);
    	freopen("fight.out","w",stdout);
    	input();
    	init();
    	work();
    	printf("%lld
    ",ans);
    	return 0;
    }
    
    

    摆渡车

    题目描述

    有 n 名同学要乘坐摆渡车从人大附中前往人民大学,第 i 位同学在第 ti分钟去 等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、 把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费m分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。

    凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?

    注意:摆渡车回到人大附中后可以即刻出发。

    输入格式

    第一行包含两个正整数 n,m,以一个空格分开,分别代表等车人数和摆渡车往返 一趟的时间。

    第二行包含 n 个正整数,相邻两数之间以一个空格分隔,第 i 个非负整数 ti代 表第 i 个同学到达车站的时刻。

    输出格式

    输出一行,一个整数,表示所有同学等车时间之和的最小值(单位:分钟)。

    样例数据

    input1

    5 1 
    3 4 4 3 5
    

    output1

    0
    

    input2

    5 5 
    11 13 1 5 5
    

    output2

    4
    

    样例说明

    样例1:

    同学 1 和同学 4 在第 3 分钟开始等车,等待 0 分钟,在第 3 分钟乘坐摆渡车出发。摆渡车在第 4分钟回到人大附中。

    同学 2 和同学 3 在第 4 分钟开始等车,等待 0 分钟,在第 4 分钟乘坐摆渡车 出发。摆渡车在第 5 分钟回到人大附中。

    同学 5 在第 5 分钟开始等车,等待 0 分钟,在第 5 分钟乘坐摆渡车出发。自此 所有同学都被送到人民大学。总等待时间为 0。

    样例2:

    同学 3 在第 1 分钟开始等车,等待 0 分钟,在第 1 分钟乘坐摆渡车出发。摆渡 车在第 6分钟回到人大附中。

    同学 4 和同学 5 在第 5 分钟开始等车,等待 1 分钟,在第 6 分钟乘坐摆渡车 出发。摆渡车在第 11 分钟回到人大附中。

    同学 1 在第 11 分钟开始等车,等待 2 分钟;同学 2 在第 13 分钟开始等车, 等待 0 分钟。他/她们在第 13 分钟乘坐摆渡车出发。自此所有同学都被送到人民大学。 总等待时间为 4。

    可以证明,没有总等待时间小于 4 的方案。

    数据规模与约定

    对于%10 的数据,(n≤10,m=1,0≤ti≤100。)

    对于%30 的数据,(n≤20,m≤2,0≤ti≤100。)

    对于%50 的数据,(n≤500,m≤100,0≤ti≤10^4。)

    另有%20 的数据,(n≤500,m≤10,0≤ti≤4×10^6。)

    对于%100 的数据,(n≤500,m≤100,0≤ti≤4×10^6。)

    时间限制:2s

    空间限制:256MB

    解析

    这就是今年的毒瘤T3了,可以说是近几年来最难的一道题,比今年T4还难。
    可是这道题的题面竟然异常简洁,让人一看就懂。有没有人像我一样一看就觉得简单的(`・ω・´),然后就死在这了。
    首先按时间排序,这是一定的。最容易让人想到的就是(O(n^3))(dp)了,设(f[i])代表前(i)个同学到达的最小等待时间总和,那么暴力转移是不会超时的。但是考场上怎么都不会想到的是,这样的dp在转移时的花费是会计算错误的,而且小数据基本调不出来,主要原因是:我们不知道在转移第(i)位同学时车是什么时候到的,也就是不知道转移的出发时间。所以在大数据上,一维的(dp)就基本全错了。
    但是这个错误算法也给我们一个启示,我们就可以借此来改进dp了。设(f[i][j])代表前i个同学,第(i)个同学在第(t[i]+j)分钟到达的最小等待时间总和,这样的状态就不会出错。其实,这样设置后我们可以很简单地划分阶段,(f[i][j])不需要从(f[k][l])转移,只需要从(f[i-1][k])转移即可,因为(i)个同学必然属于以下两类中的一类:

    1.第(i)个同学和第(i-1)个同学坐同一辆车
    2.第(i)个同学和第(i-1)个同学不坐同一辆车

    那么我们来讨论转移的细节。
    先看一个最基本的问题,两位到达时间差超过(m)且到达时间相邻的同学必然不坐同一辆车,因为他们的时间间隔足以让车往返一趟。
    那么状态第二维的上限只需要开到(2m)大小即可,达到上限的情况就是和他同时来的一个同学直接坐车走了,花费m分钟,车回来后再送他,又花费m分钟,在第(t[i]+2m)分钟时,前i位同学全部到达。
    此时,我们利用阶段转移:

    [1. if(t[i-1]+k==t[i]+j) f[i][j]=min(f[i][j],f[i-1][k]+j) \2. if(t[i-1]+k+m<=t[i]+j) f[i][j]=min(f[i][j],f[i-1][k]+j) ]

    那么三重循环就解决了呀。

    (Code:)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=580,M=580,T=4*1e6+80,INF=0x3f3f3f3f;
    int n,m,t[N]={},f[N][2*M]={},ans=INF;
    inline void input(void)
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&t[i]);
    } 
    inline void init(void)
    {
    	sort(t+1,t+n+1);
    	for(int i=1;i<=m*2;i++)f[1][i]=i;
    }
    inline void solve(void)
    {
    	for(int i=2;i<=n;i++)
    	{
    		for(int j=0;j<2*m;j++)
    		{
    			f[i][j]=INF;
    			for(int k=0;k<2*m;k++)
    			{
    				if(t[i-1]+k==t[i]+j||t[i-1]+k+m<=t[i]+j)f[i][j]=min(f[i][j],f[i-1][k]+j);
    			}
    			if(i==n)ans=min(ans,f[i][j]);
    		}
    	}
    }
    int main(void)
    {
    	freopen("bus.in","r",stdin);
    	freopen("bus.out","w",stdout);
    	input();
    	init();
    	solve();
    	printf("%d
    ",ans);
    	return 0;
    }
    
    

    对称二叉树

    题目描述

    一棵有点权的有根树如果满足以下条件,则被轩轩称为对称二叉树:

    1.二叉树;

    2.将这棵树所有节点的左右子树交换,新树和原树对应位置的结构相同且点权相等。

    下图中节点内的数字为权值,节点外的 id 表示节点编号。

    现在给出一棵二叉树,希望你找出它的一棵子树,该子树为对称二叉树,且节点数 最多。请输出这棵子树的节点数。

    注意:只有树根的树也是对称二叉树。本题中约定,以节点 T 为子树根的一棵“子 树”指的是:节点T 和它的全部后代节点构成的二叉树。

    输入格式

    第一行一个正整数 n,表示给定的树的节点的数目,规定节点编号1∼n,其中节点 1 是树根。

    第二行 n 个正整数,用一个空格分隔,第 i 个正整数vi代表节点 i的权值。

    接下来 n 行,每行两个正整数 li,ri,分别表示节点 i的左右孩子的编号。如果不存在左 / 右孩子,则以 -1表示。两个数之间用一个空格隔开。

    输出格式

    输出文件共一行,包含一个整数,表示给定的树的最大对称二叉子树的节点数。

    样例数据

    input1

    2 
    1 3 
    2 -1 
    -1 -1
    

    output1

    1
    

    input2

    10 
    2 2 5 5 5 5 4 4 2 3 
    9 10 
    -1 -1 
    -1 -1 
    -1 -1 
    -1 -1 
    -1 2 
    3 4 
    5 6 
    -1 -1 
    7 8
    

    output2

    3
    

    样例说明

    样例1:

    最大的对称二叉子树为以节点 2 为树根的子树,节点数为 1。

    样例2:

    最大的对称二叉子树为以节点 7 为树根的子树,节点数为 3。

    数据规模与约定

    共 25 个测试点。

    (v_i≤1000。)

    测试点 (1∼3,n≤10),保证根结点的左子树的所有节点都没有右孩子,根结点的右 子树的所有节点都没有左孩子。

    测试点 (4∼8,n≤10)

    测试点 (9∼12,n≤10^5),保证输入是一棵“满二叉树” 。

    测试点 (13∼16,n≤10^5),保证输入是一棵“完全二叉树”。

    测试点 (17∼20,n≤10^5),保证输入的树的点权均为 1。

    测试点 (21∼25,n≤10^6)

    时间限制:1s

    空间限制:256MB

    本题约定

    层次:节点的层次从根开始定义起,根为第一层,根的孩子为第二层。树中任一节 点的层次等于其父亲节点的层次加 1。

    树的深度:树中节点的最大层次称为树的深度。

    满二叉树:设二叉树的深度为 h,且二叉树有 2h-1个节点,这就是满二叉树。

    完全二叉树:设二叉树的深度为 h,除第 h 层外,其它各层的结点数都达到最大 个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

    解析

    这大概就是和T3放反了的T4吧。一看题面,大部分同学都被吓住了吧,一堆树转来转去的,大概又是什么高级算法。
    实际上,这还真就不是了...
    总的思路,就是暴力。先暴力枚举每一个点作为根节点,然后判断以该节点作为根的子树是否符合要求,然后就可以更新答案了。
    重点在于怎么判断呢?用样例推一下,可以发现规律:
    以一个深度为三的二叉树为例,如果符合要求,根节点的左右儿子相等,且满足:

    1.左儿子的左儿子 等于 右儿子的右儿子
    2.左儿子的右儿子 等于 右儿子的左儿子
    3.如果需要比较的两个节点均为空,视为他们相等

    那么我们称该根节点的左右儿子为一个符合(Check)要求的点对((u,v)),也就是说点对((u,v))符合要求,他满足:

    1.(u=v)
    2.(leftson_u=rightson_v)
    3.(rightson_u=leftson_v)
    4.如果需要比较的两个节点均为空,视为他们相等

    再以深度为四的二叉树为例,如果符合要求,他的左右儿子需满足(Check)的要求,且:左儿子的左儿子右儿子的右儿子满足(Check)左儿子的右儿子右儿子的左儿子满足(Check)
    发现三层的二叉树的规律很简单,但如果你发现(Check)这个规则是递归适用的,那么这道题就做出来了,这个就是判断的规则。
    如果以一个节点为根它满足对称二叉树的要求,那么用以他为根的子树大小更新答案即可,子树大小用dfs暴力预处理。
    分类讨论满二叉树和链的情况,时间复杂度是(O(nlog_2n)),足以完成本题。
    当然还有更高级的算法,例如中序遍历求最长回文子串,树哈希等,这里不再提及。

    (Code:)

    #include<bits/stdc++.h>
    using namespace std;
    inline void read(int &k)
    {
    	int x=0,w=0;char ch;
    	while(!isdigit(ch))w|=ch=='-',ch=getchar();
    	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    	k=(w?-x:x);return;
    }
    int n,val[1000080],son[1000080][2],size[1000080],ans=0; 
    inline void input(void)
    {
    	read(n);
    	for(int i=1;i<=n;i++)read(val[i]);
    	for(int i=1;i<=n;i++)
    	{
    		read(son[i][0]);
    		read(son[i][1]);
    	}
    }
    inline void get_size(int root)
    {
    	size[root]=1;
    	if(son[root][0]!=-1)
    	{
    		get_size(son[root][0]);
    		size[root]+=size[son[root][0]];
    	}
    	if(son[root][1]!=-1)
    	{
    		get_size(son[root][1]);
    		size[root]+=size[son[root][1]];
    	}
    }
    inline bool check(int x,int y)
    {
    	if(x==-1&&y==-1)return true;
    	if(x!=-1&&y!=-1&&val[x]==val[y]&&check(son[x][0],son[y][1])&&check(son[x][1],son[y][0]))return true;
    	return false;
    }
    int main(void)
    {
    	freopen("tree.in","r",stdin);
    	freopen("tree.out","w",stdout);
    	input();
    	get_size(1);
    	for(int i=1;i<=n;i++)
    	{
    		int x=son[i][0],y=son[i][1];
    		if(check(x,y))
    		{
    			ans=max(ans,size[i]);
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
    

    <后记>
    这一次NOIP没考好,主要是时间安排不对,在T3上花的时间太多了,确实也值得反思。多参加模拟赛,训练应考能力和心态吧。

  • 相关阅读:
    要使用springtest来进行单元测试 否则将无法注入 applicationContext.xml
    关键词的重要性
    发卡可以以交叉方式佩戴来增强可爱性
    红毛类型
    oracle学习总结4
    对springMVC的简单理解
    理解RESTful架构
    项目移植过程中报:“Project facet Java version 1.7 is not supported.” 错误
    svn服务器的搭建
    oracle学习总结3
  • 原文地址:https://www.cnblogs.com/Parsnip/p/10113476.html
Copyright © 2011-2022 走看看