【三】【算法】P1007 独木桥,P1012 [NOIP1998 提高组] 拼数,P1019 [NOIP2000 提高组] 单词接龙

P1007 独木桥

独木桥

题目背景

战争已经进入到紧要时间。你是运输小队长,正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激,于是命令你的士兵们到前方的一座独木桥上欣赏风景,而你留在桥下欣赏士兵们。士兵们十分愤怒,因为这座独木桥十分狭窄,只能容纳 1 1 1 个人通过。假如有 2 2 2 个人相向而行在桥上相遇,那么他们 2 2 2 个人将无法绕过对方,只能有 1 1 1 个人回头下桥,让另一个人先通过。但是,可以有多个人同时呆在同一个位置。

题目描述

突然,你收到从指挥部发来的信息,敌军的轰炸机正朝着你所在的独木桥飞来!为了安全,你的部队必须撤下独木桥。独木桥的长度为 L L L,士兵们只能呆在坐标为整数的地方。所有士兵的速度都为 1 1 1,但一个士兵某一时刻来到了坐标为 0 0 0 L + 1 L+1 L+1 的位置,他就离开了独木桥。

每个士兵都有一个初始面对的方向,他们会以匀速朝着这个方向行走,中途不会自己改变方向。但是,如果两个士兵面对面相遇,他们无法彼此通过对方,于是就分别转身,继续行走。转身不需要任何的时间。

由于先前的愤怒,你已不能控制你的士兵。甚至,你连每个士兵初始面对的方向都不知道。因此,你想要知道你的部队最少需要多少时间就可能全部撤离独木桥。另外,总部也在安排阻拦敌人的进攻,因此你还需要知道你的部队最多需要多少时间才能全部撤离独木桥。

输入格式

第一行共一个整数 L L L,表示独木桥的长度。桥上的坐标为 1 , 2 , ⋯   , L 1, 2, \cdots, L 1,2,,L

第二行共一个整数 N N N,表示初始时留在桥上的士兵数目。

第三行共有 N N N 个整数,分别表示每个士兵的初始坐标。

输出格式

共一行,输出 2 2 2 个整数,分别表示部队撤离独木桥的最小时间和最大时间。 2 2 2 个整数由一个空格符分开。

样例 #1

样例输入 #1

4 2 1 3

样例输出 #1

2 4

提示

对于 100 % 100\% 100% 的数据,满足初始时,没有两个士兵同在一个坐标, 1 ≤ L ≤ 5 × 1 0 3 1\le L\le5\times 10^3 1L5×103 0 ≤ N ≤ 5 × 1 0 3 0\le N\le5\times10^3 0N5×103,且数据保证 N ≤ L N\le L NL

代码

#include<bits/stdc++.h>
using namespace std;
int main() {
	int L, N;
	cin >> L >> N;
	int min_time = 0, max_time = 0;

	for (int i = 0; i < N; i++) {
		int pos; cin >> pos;
		int time1 = pos; int time2 = L + 1 - pos;
		min_time=max(min(time1, time2),min_time);
		max_time=max(max(time1, time2),max_time);

	}

	cout << min_time << " " << max_time << endl;
}
  • 思路
    每一个士兵地位是相等的,他们每一个人没有本质的区别,可以想象当两个士兵迎面对撞时,他们各自都要掉头前进,而他们地位相同,可以理解为士兵没有掉头,而是直接穿越过去,犹如一个幽灵体,无视体积碰撞,因此对于每一个士兵,都可以依靠他所在的位置计算出他所需要花费的最大时间和最小时间.
  • 目标
    我们需要计算花费的最小时间和花费的最大时间,所有士兵是一起行动的,并且处于幽灵体无视体积碰撞,花费的最小时间取决于所有士兵的花费最小时间中花费的最大时间,花费最大时间取决于所有士兵的花费最大时间中花费的最大时间.
    听起来有一点绕口,我们可以用一张图进行解释.
    在这里插入图片描述
  • 转换
    问题被转化为求一组最小值数据中最大值和一组最大值数据中最大值.

P1012 [NOIP1998 提高组] 拼数

[NOIP1998 提高组] 拼数

题目描述

设有 n n n 个正整数 a 1 … a n a_1 \dots a_n a1an,将它们联接成一排,相邻数字首尾相接,组成一个最大的整数。

输入格式

第一行有一个整数,表示数字个数 n n n

第二行有 n n n 个整数,表示给出的 n n n 个整数 a i a_i ai

输出格式

一个正整数,表示最大的整数

样例 #1

样例输入 #1

3 13 312 343

样例输出 #1

34331213

样例 #2

样例输入 #2

4 7 13 4 246

样例输出 #2

7424613

提示

对于全部的测试点,保证 1 ≤ n ≤ 20 1 \leq n \leq 20 1n20 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1ai109

NOIP1998 提高组 第二题

代码

#include<bits/stdc++.h>
using namespace std;
#if 1
bool com(int a, int b) {

	int tmpa = a, tmpb = b;
	int lenA = 1, lenB = 1;
	while (tmpa /= 10) lenA++;
	while (tmpb /= 10) lenB++;

	tmpa = a; tmpb = b;
	while (1) {

		int topA = tmpa / (int)pow(10, lenA - 1);
		int topB = tmpb / (int)pow(10, lenB - 1);

		if (topA > topB) return true; 
		if (topA < topB) return false; 

		
		lenA--;
		lenB--;

		if (lenA == 0 && lenB == 0) break; 
	}

	return false; 
}
#endif
#if 0
bool com(int a, int b) {
	string stra = to_string(a);
	string strb = to_string(b);
	return stra + strb > strb + stra;
}
#endif
int  main() {
	int n; cin >> n;
	vector<int>a;
	for (int i = 0; i < n; i++) {
		int nums; cin >> nums;
		a.push_back(nums);
	}
	sort(a.begin(), a.end(), com);
	for (auto x : a) {
		cout << x;
	}
	cout << endl;

}
  • 思路
    字符串判断大小是怎么判断的?先判断两个字符串的第一个字符大小,哪个字符大,哪个字符串就大,如果相同就判断第二个字符大小,依次类推.
    这道题目需要我们判断数字的大小,并且按照这种字符串的判断方式去判断.一开始我想通过一个函数去判断数字大小,但是这种代码有些用例时间复杂度过不去.因此我们可以利用内置的string判断大小方式去判断数字,只需要把数字转化为字符串即可.

P1019 [NOIP2000 提高组] 单词接龙

[NOIP2000 提高组] 单词接龙

题目背景

注意:本题为上古 NOIP 原题,不保证存在靠谱的做法能通过该数据范围下的所有数据。

本题为搜索题,本题不接受 hack 数据。关于此类题目的详细内容

NOIP2000 提高组 T3

题目描述

单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beastastonish,如果接成一条龙则变为 beastonish,另外相邻的两部分不能存在包含关系,例如 atatide 间不能相连。

输入格式

输入的第一行为一个单独的整数 n n n 表示单词数,以下 n n n 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。

输出格式

只需输出以此字母开头的最长的“龙”的长度。

样例 #1

样例输入 #1

5 at touch cheat choose tact a

样例输出 #1

23

提示

样例解释:连成的“龙”为 atoucheatactactouchoose

n ≤ 20 n \le 20 n20

代码

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

#if 1
int n;
char start;
vector<string> a;
vector<int> used;
int ret = 0;

int f(string str1, string str2) {
	for (int i = 1; i <= min(str1.size(), str2.size()); i++) {
		bool flag = true;
		for (int j = 0; j < i; j++) {
			if (str1[str1.size() - i + j] != str2[j]) {
				flag = false;
				break;
			}
		}
		if (flag) return i;
	}
	return 0;
}

void dfs(string str_now) {
	ret = max(ret, (int)str_now.size());
	for (int i = 0; i < a.size(); i++) {
		if (used[i] >= 2) continue;
		int c = f(str_now, a[i]);
		if (c > 0) {
			used[i]++;
			dfs(str_now + a[i].substr(c));
			used[i]--;
		}
	}
}

int main() {
	cin >> n;
	a.resize(n);
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	cin >> start;

	used.resize(n, 0);


	for (int i = 0; i < n; i++) {
		if (a[i][0] == start) {
			used[i]++;
			dfs(a[i]);
			used[i]--;
		}
	}

	cout << ret << endl;
}
#endif

  • 思路
    暴力递归求解,我们首先有许多个字符串,对于每一个字符串我们最多使用两次,那么我们用一个used数组记录所有的字符串使用的次数情况,接着是start字符,是我们的龙头.
    我们递归每一种情况,对于每一种情况得到的龙的长度记录,求这些长度的最大值.
  • 递归含义
    dfs递归的含义是当前节点所拼接的字符串是str_now,然后遍历我们拥有的字符串,需要满足两个条件就可以拼接,第一个条件是字符串使用次数没有超过两次,第二个条件是可以拼接.
  • 拼接判断
    是否可以拼接我们可以写一个函数进行判断,f函数的含义就是判断str1,str2是否满足拼接条件.
  • f函数解释
int f(string str1, string str2) {
    // 遍历从1到最小字符串长度的所有可能的重叠长度
    for (int i = 1; i <= min(str1.size(), str2.size()); i++) {
        bool flag = true;
        // 检查两个字符串是否有长度为i的相同部分
        for (int j = 0; j < i; j++) {
            // str1的后i个字符是否和str2的前i个字符相同
            if (str1[str1.size() - i + j] != str2[j]) {
                // 如果不相同,重置flag为false并跳出循环
                flag = false;
                break;
            }
        }
        // 如果找到了一个长度为i的相同部分,则返回该长度
        if (flag) return i;
    }
    // 如果没有找到任何重叠部分,返回0
    return 0;
}

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妖精七七_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值