2018年NOIP普及组复赛题目解析

290 阅读3分钟

一、标题统计

image.png 题目要求我们要输入一个字符串,并计算除空格外的字符个数:

image.png 从给定的数据规模可知,我们最多只需要输入5个字符长度的字符串即可。代码实现如下

#include <iostream>
using namespace std;

int main(){
	string s;
	int cnt=0;
	getline(cin, s);
	int n = s.length();
	if(n == 0 ) return 0;
	for(int i=0; i<n; ++i){
		if(s[i] != ' ') ++cnt;
	}
	cout << cnt << endl;  
} 

参考答案代码:

#include <iostream>
#include <cstdlib>
#include <string>

int main() {
    freopen("./title/title4.in", "r", stdin);
    freopen("title.out", "w", stdout);

    std::string s;
    std::getline(std::cin, s);
    int cnt = 0;
    if (s.length() > 0 && s[0] != ' ') ++cnt;
    if (s.length() > 1 && s[1] != ' ') ++cnt;
    if (s.length() > 2 && s[2] != ' ') ++cnt;
    if (s.length() > 3 && s[3] != ' ') ++cnt;
    if (s.length() > 4 && s[4] != ' ') ++cnt;
    std::cout << cnt << std::endl;

    return 0;
}

二、龙虎斗

image.png

image.png 算法思想: 题目要求输出派遣的兵营编号,使双方的势力相差尽可能地少,设龙势力值为xx,虎势力值为yy,我们先要判断龙虎方谁家的势力大些,若x>yx>y则可能派往虎方,若x<yx<y则可能派往龙方,若x=yx=y则派往mm
代码如下:

#include <cstdio>
#include <climits>
// 定义长整型,防止数据溢出
typedef long long LL;

const int N = 100000;

int n, m, p1;
LL c[N], s1, s2;
// 取绝对值操作
LL ABS(LL x) { return x < 0 ? -x : x; }

int main() {
    //输出n个节点并赋予权值
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%lld", &c[i]);
    }
    scanf("%d%d%lld%lld", &m, &p1, &s1, &s2);
    //(m - p1) * s1代表天降神兵,为负表示在虎方,为正表示在龙方。
    //best定义双方实力差距,k代表输出的兵营
    LL sum = (m - p1) * s1, best = LLONG_MAX; int k = -1;
    //此时sum代表双方的势力差距
    for (int i = 1; i <= n; ++i) {
        sum += (m - i) * c[i];
    }
    //求取最小的派遣方案,并输出派遣的兵营号
    for (int i = 1; i <= n; ++i) {
        LL tmp = ABS(sum + (m - i) * s2);
        if (tmp < best) {
            best = tmp;
            k = i;
        }
    }
    printf("%d\n", k);

    return 0;
}

三、摆渡车

image.png 算法思想: 题目要求最后一班车出发后所有人等待的最短时间。我们假设最后一班车出发的时间为t[n][t[n],t[n]+m)t[n]∈[t[n],t[n]+m),则倒数第二班车出发的时间取值应该为(t[n]2m,t[n]m](t[n]-2*m,t[n]-m]这样才能保证最后哦一班车在t[n]时间点准时出发,若倒数第二班车早于t[n]2mt[n]-2*m,若期间无人到达则无需发车,若期间有人则完全可以先发车才能使所有的等待时间最短,故若期间有人其必定不是倒数第二班车,若期间无人则时间距离可以设置为2*m,不影响最终结果。

image.png

image.png 代码实现:

#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 500, M = 100, T = 2*M*N, INF = 0X7FFFFFFF;
int n, m, ans=INF, t[N], c[N], d[T], wt[T], nums[T], sum[T];
// c[i]代表连续两个到达的人之间的时间间隔  
// 数组wt[T],wt[i]表示在时间点i之前包含时间点i到达的所有人,乘坐第i分钟出发的车,总的等待时间;
// nums[i]记录在时间点i到达的人数。
// sum[i]记录在时间点i之前包含时间点i到达的人数。 
// d[i]表示最后一班车在i分钟发车,最小的总等待时间;
// d[i] = min(d[j] + wait(j+1, i)), j是上一班车的发车时间,j∈(i-2m, i-m];
// wait(i,j)表示在j和i之间到达的人,搭乘i分钟出发的车所需的等待时间。wait(i,j)=wt[i]-wt[j]-(i-j)*sum[j] 
// ans = min(d[i]); i[t[n], t[n]+m) 

int main(){
	scanf("%d %d", &n, &m);
	// 存储n个人到达的时间点 
	for(int i=1; i<=n; i++) scanf("%d", &t[i]);
	// 将n个人到达的时间点进行有序排列 
	sort(t+1, t+1+n);
	// 差分数组
	for(int i=1;i<=n;i++) c[i] = t[i] - t[i-1];
	// t[1]设为2*m为了保证后面计算j = i-2*m+1 > 0, 且t[1]代表第一班车的发车时间不影响最后结果 
	t[1] = 2 * m, nums[t[1]] = 1 ;
	// 枚举连续两个时间段的距离,若大于2*m可以直接取值为2*m不影响结果 
	// 并为每个人到达的时间点进行重新定义,主要因为t[1]值换了 
	for(int i=2;i<=n;i++){
		if(c[i]>2*m) c[i]=2*m;
		t[i]=t[i-1]+c[i];
		nums[t[i]]++;
	}
	// 枚举计算最后一班车的发车时间 
	for(int i=t[1];i<t[n]+m;i++){
		// 没延后一分钟,前面的人sum[i-1]就多等一分钟 
		wt[i] = wt[i-1] + sum[i-1]*1;
		sum[i] = sum[i-1] + nums[i];
		d[i] = wt[i]; 
	}
	for(int i=t[1];i<t[n]+m;i++)
		for(int j=i-2*m+1;j<=i-m;j++) 
			d[i]=min(d[i], d[j]+wt[i]-wt[j]-(i-j)*sum[j]);
	
	for(int i=t[n];i<t[n]+m;i++) ans=min(ans, d[i]);
	printf("%d", ans);
	return 0; 
} 

四、对称二叉树

image.png 算法思想:对称二叉树其实就是对应的左右子树互换位置与原来一样,即左子树节点的值等于右子树节点的值。算法实现如下:

#include <iostream>
#include <algorithm>

using namespace std;

// 依题意知:n<=10^6 
const int Size = 1e6+10;
const int INF = 0x3f3f3f3f;
# define ll long long;

// 根据n个节点创建树 
struct Tree{
	int v, Left_Child, Right_Child;
}tree[Size];

void DFS(int L, int R);
// n为节点树,sum为对称二叉树的根节点树,ans为结果 
int n, sum=1, ans=1;
//递归标值 
bool F=true;

int main(){
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &tree[i].v);
	for (int i = 1; i<=n;i++) scanf("%d%d", &tree[i].Left_Child, &tree[i].Right_Child);
	
	for (int i=1; i<=n; i++){
		F = true;
		sum=1;
		DFS(tree[i].Left_Child, tree[i].Right_Child);
		if(F) ans = max(ans, sum);
	}
	cout << ans;
	return 0;
}

//深度遍历二叉树 
void DFS(int L, int R){
	if(F == false) return;
	if (L == -1 && R == -1) return;
	if ((L == -1 && R !=-1) || (L != -1 && R == -1)){
		F = false;
		return;
	}
	
	if (tree[L].v == tree[R].v){
		sum+=2;
		DFS(tree[L].Left_Child, tree[R].Right_Child);
		DFS(tree[L].Right_Child, tree[R].Left_Child);
	}
	else{
		F = false;
		return;
	} 
}