题目

说明:点击题目名称即可跳转原题链接 🙂

题号题目知识点难度
T1Cantor 表数学,模拟入门
T2回文数高精度、模拟普及-
T3旅行家的预算贪心,模拟普及+/提高
1999-NOIP-普及组:复赛真题及解析

Cantor 表

思路

  1. 找规律,根据题目编号的方式调整表格,分析发现,每一行中元素的分子、分母和为当前行号 + 1。
  1. 从左到右分子逐渐减小,分母逐渐增大。因此如果能够确定第 n 个元素在第几行第几个位置,就能确定这个元素。根据第 i 行有 i 个元素,通过循环累加每行元素的数量,就可以找到第 n 个元素具体所在行和列。
  2. 注意:由于数据呈 Z 字形排列,需要区分奇偶行数数的顺序,奇数行从左开始,偶数行从右开始。

代码

#include <bits/stdc++.h>
using namespace std;

int n; 
int main(){
        cin >> n;
        int i = 1;
        for(; n > i; i++){ // 确定所求是第 i 行第 n 个元素
                n -= i;
        } 
        if(i % 2){ // 奇数行:从左往右数,分母是 n 
                cout << i + 1 - n << "/" << n;
        } 
        else{ // 偶数行:从右往左数,分子是 n 
                cout << n << "/" << i + 1 - n;
        } 
        return 0;
} 

回文数

思路

  1. 注意进制 N 可能是 16,因此输入 M 可能会带有字母,需要作为字符串输入并进行对应数字转换,将结果存放在数组 a[]。
  2. 对 a[] 进行扫描判断是否是回文数,只要还不是,就进行 N 进制下高精度 a[] 与反向 a[] 的加法,同时记录操作次数 + 1。
  3. 当操作次数超过 30 时,直接输出 “Impossible!” 并结束程序,否则若 30 次以内得到了回文数,输出操作次数。

代码

#include <bits/stdc++.h>
using namespace std;

int n, s, a[150], k;
string m;

bool ishw(int len){ // 判断是否是回文数 
        for(int i = 1; i <= len / 2; i++){
                if(a[i] != a[1 + len - i]){ // 一旦发现有一对不等 
                        return 0; // 返回不是 
                }
        }
        return 1; // 全都比对完,返回是 
}

int add(int len){ // 更新 a[] 为加上反置数后的结果,返回更新后长度 
        int b[150] = {0};
        for(int i = 1; i <= len; i++){
                b[i] = b[i] + a[i] + a[len + 1 - i];  
                b[i + 1] = b[i] / n; // n 进制下进位 
                b[i] = b[i] % n;
        }
        if(b[len + 1]){ // 处理最高位进位的情况 
                len++;
        }
        for(int i = 1; i <= len; i++){
                a[i] = b[i];
        }
        return len; // 返回加法操作后 a 数组长度 
}
int main(){
        cin >> n >> m;
        for(int i = 0;  m[i]; i++){ // 注意可能会是 16 进制 
                int t = m[i] >= 'A' ? m[i] - 'A' + 10 : m[i] - '0';
                a[++k] = t;
        }
        while(!ishw(k)){
                s++;
                // 更新 a 为 N 进制下加上反置数的和 
                k = add(k); // 更新长度 
                if(s > 30){
                        cout << "Impossible!";
                        return 0;
                }
        }
        cout << "STEP=" << s;
        return 0;
}

旅行家的预算

思路

  1. 贪心策略,应该在尽可能便宜的站点加油。维护数组 a[i] 记录到第 i 个加油站点最便宜的站点,那么对于到达第 i 个站点所需的油量,是在 a[i – 1] 处实现的。
  2. 把终点设为第 n + 1 个站点,它的最优加油站点解同 a[n],然后依次枚举每个站点的距离,计算相邻站点所需油量,超出油箱容量就是无解,没超出用 a[i-1] 站点进行到达第 i 个站点路程的加油,即用价格 p[a[i-1]] 计算d[i-1] ~ d[i] 的路程花费。
  3. 注意:如果仅按上述解法提交只能得 75 分,因为它假设了在左端最便宜站点加油的正确性,但除此之外还要考虑油箱容量的问题,有没有可能你想得很美,但最便宜的站点加油容量不够加到能到此处呢?(提示到这儿,剩下的自己想 :P)。

代码

#include <bits/stdc++.h>
using namespace std;

double d1, c, d2, d[10], p[10], b[10], ans, m;
int n, a[10]; // a[i] 记录到第 i 个站点最便宜的加油站 

void add(int k, double x){ // 在站点 k 加 x 升油
	b[k] += x;
	ans += x * p[k]; 
}

int main(){
	scanf("%lf %lf %lf %lf %d", &d1, &c, &d2, &p[0], &n);
	for(int i = 1; i <= n; i++){
		scanf("%lf %lf", &d[i], &p[i]);
		a[i] = (p[i] < p[a[i-1]] ? i : a[i-1]);
 	}
 	d[n+1] = d1, a[n + 1] = a[n];
 	for(int i = 1; i <= n + 1; i++){
 		double t = (d[i] - d[i - 1]) / d2;
 		if(t > c){
 			printf("No Solution");
 			return 0;
		}
		int k = a[i - 1];  
		if(b[k] + t > c){ // 在左边最便宜的点加油容量不够
			double x = c - b[k]; // 最多只能加 c-b[k] 
			add(k, x);
			double r = (d[i] - d[i - 1] - x * d2) / d2; // 剩余要加的油量 
			int minl = i - 1;  // 选左边最便宜且余量够的站点加剩余 r 升油 
			for(int j = i - 1; j >= 0; j--){
				if(b[j] + r <= c && p[j] < p[minl]){
					minl = j;
				}
			}  
			add(minl, r); 
		}
		else{ // 在最便宜的站点加油容量够,直接加满 t 
			add(k, t); 
		}
	}
 	printf("%.2lf", ans);
	return 0;
} 

代码2

来自我的学生,一个上海四年级的小朋友,很简洁,分享一下 😀

#include<bits/stdc++.h>
using namespace std;
double d1,c,d2,p,d[10],v[10],l,mi;
int n;
int main(){
	cin>>d1>>c>>d2>>p>>n;
	for(int i=1;i<=n;i++)cin>>d[i]>>v[i];
	d[n+1]=d1;v[0]=p;
	for(int i=1;i<=n;i++){
		if(d[i+1]-d[i]>c*d2){
			cout<<"No Solution";
			return 0;
		}
	}
	int j;
	for(int i=0;i<=n;i=j){
		for(j=i+1;j<=n+1;j++){
			if(d[j]-d[i]>c*d2){
				j--;
				break;
			}
			if(v[j]<v[i])break;
		}
		if(v[j]<v[i])mi+=((d[j]-d[i])/d2-l)*v[i],l=0;
		else mi+=(c-l)*v[i],l=c-(d[j]-d[i])/d2;
	}
	printf("%.2lf\n",mi);
	return 0;
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注