目录
1124 Raffle for Weibo Followers
1141 PAT Ranking of Institutions
1153 Decode Registration Card of PAT
1001 A+B Format
题型:模拟题
思路(low):独立正负号,卡1000与100000位在卡位前用字符串获取并在前加“,”
思路(high):a + b =》 转成字符串 =》 加入“,”(注意最后一位和最后的下一位是-的情况不加“,”即可);
C++实现
#include <iostream>
using namespace std;
int main() {
int a,b;
cin >> a >> b;
int c = a + b;
string num = to_string(c); // 转换为字符串的方法:to_string
string res;
for (int i = num.size() - 1, j = 0; i >= 0; i--) {
res = num[i] + res;
j++;
if (j % 3 == 0 && i && num[i - 1] != '-') res = "," + res; // 三位数 前一位是最后一位 前一位不是负号
}
cout << res << endl;
return 0;
}
1005 Spell It Right
题型:模拟题
思路:求和字符串读取,easy
#include<iostream>
using namespace std;
int main() {
string N;
cin >> N;
int sum = 0;
for (auto c : N) sum += c - '0'; // 优化遍历
string s = to_string(sum);
char word[10][10] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; // 这里二维是因为每个字符串就是一个字符数组
cout << word[s[0] - '0'];
for (int i = 1; i < s.size(); i++) cout << ' ' << word[s[i] - '0']; // 避免行位多一个空格
return 0;
}
1006 Sign In and Sign Out
题型:模拟题
思路:字符串判断留值即可
#include<iostream>
using namespace std;
int main() {
int M;
cin >> M;
string minID, maxID;
string open_time, close_time; // 可能会想给赋个初始值,但实际情况不如用for去判断是否首项
for (int i = 0; i < M; i++) {
string id, in, out;
cin >> id >> in >> out;
if (!i || in < open_time) {
open_time = in;
minID = id;
}
if (!i || out > close_time) {
close_time = out;
maxID = id;
}
}
cout << minID << " " << maxID;
return 0;
}
/*
有唯一性:no two persons sign in or out at the same moment.
*/
1035 Password
题型:模拟题
思路:字符串读,字符串替换后比较,数组处理
#include<iostream>
using namespace std;
const int N = 1001; // 留一个预留空间(C++中要给数组不赋值的预定义就需设置为常量)
string ids[N], change_pwds[N]; // 必须要存,不然怎么把改变的密码id数打印在第一位
string change (string str) {
string res;
for (auto c : str) {
if (c == '1') res += '@';
else if (c == '0') res += '%';
else if (c == 'l') res += 'L';
else if (c == 'O') res += 'o';
else res += c;
}
return res;
}
int main() {
int M;
int k = 0;
cin >> M;
for (int i = 0; i < M; i++) {
string id, pwd;
cin >> id >> pwd;
string change_pwd = change(pwd);
if (change_pwd != pwd) {
ids[k] = id;
change_pwds[k] = change_pwd;
k++;
}
}
// 表示没有困惑字符
if (!k) {
if (M == 1) printf("There is 1 account and no account is modified\n");
else printf("There are %d accounts and no account is modified\n", M); // 注意is和are,负数的acount和换行
} else {
cout << k << endl;
for (int i = 0; i < k; i++) cout << ids[i] << ' ' << change_pwds[i] << endl;
}
return 0;
}
/*
会发现通过直接比较的实现比较不好写
可以用一个替换函数,判断它们是否相等,不相等则存入一个数组
*/
1036 Boys vs Girls
题型:模拟题
思路:判断并存值记录,依次判空存储
#include<iostream>
using namespace std;
int main() {
int N;
cin >> N;
string idm, namem, idf, namef;
int minScore = 101;
int maxScore = -1;
for (int i = 0; i < N; i++) {
string name, gd, id;
int score;
cin >> name >> gd >> id >> score;
if (gd == "M" && (idm.empty() || score < minScore)) {
minScore = score;
idm = id;
namem = name;
}
if (gd == "F" && (idf.empty() || score > maxScore)) {
maxScore = score;
idf = id;
namef = name;
}
}
if (idf.empty()) puts("Absent");
else cout << namef << ' ' << idf << endl;
if (idm.empty()) puts("Absent");
else cout << namem << ' ' << idm << endl;
if (!idf.empty() && !idm.empty()) cout << abs(maxScore - minScore) << endl;
else cout << "NA" << endl;
return 0;
}
1050 String Subtraction
题型:哈希表
思路:根据S2构造哈希表,再遍历S1判断哈希表即可
做法1:如果用hash表,存入后只需O(n),比较快
函数:unordered_set的insert与count方法
#include<iostream>
#include<unordered_set>
using namespace std;
int main() {
string S1, S2, res;
getline(cin, S1); // 输入一个包含空格的字符串
getline(cin, S2);
// (1)根据S2构造哈希表
unordered_set<char> hash;
for (auto c : S2) hash.insert(c);
// (2)遍历S1判断哈希表即可
for (auto c : S1)
if (!hash.count(c))
res += c;
cout << res << endl;
return 0;
}
做法2:硬比较,不推荐(n^2比较慢)
#include<iostream>
using namespace std;
string S1, S2;
bool check(char c) {
for (auto a : S2) {
if (a == c) return true;
}
return false;
}
int main() {
string res;
getline(cin, S1); // 输入一个包含空格的字符串
getline(cin, S2);
// cin >> S1 >> S2;
for (auto c : S1) {
if (!check(c)) {
res += c;
}
}
cout << res << endl;
return 0;
}
#include <unordered_set>
#include <unordered_map>
#include <set>
#include <map>
// 基于平衡树实现:O(logn)
// 有序
set<int> a;
map<int, int> b;
multiset<int> c; // multi-可重复
multimap<int, int> d;
// 基于hash表实现:O(1)
// 无序
unordered_set<int> e;
unordered_map<int, int> f;
unordered_multiset<int> g;
unordered_multimap<int, int> h;
1071 Speech Patterns
类型:哈希表
题目:单词拼写:选出高频单词,若不止一个,则选字典序最小的,单词连续,空格等其他符号不算
思路:
(1)map表存储单词-次数,双指针遍历,构建完map表
(2)外记录单词和长,遍历map替换,长或 同长和小(C++中second表值,first表键)
#include <iostream>
#include <unordered_map>
using namespace std;
bool check(char c) { // j检查是否是需要字符
if (c >= '0' && c <= '9') return true;
if (c >= 'A' && c <= 'Z') return true;
if (c >= 'a' && c <= 'z') return true;
return false;
}
char to_lower(char c) { // 大写转小写
if (c >= 'A' && c <= 'Z') return c - 'A' + 'a';
return c;
}
int main() {
string s;
getline(cin, s);
unordered_map<string, int> map; // 单词 - 次数
for (int i = 0; i < s.size(); i++) {
if (check(s[i])) {
string word;
int j = i;
while (j < s.size() && check(s[j])) word += to_lower(s[j++]); // 双指针遍历单词
map[word]++;
i = j;
}
}
string word; // 记录结果单词
int count = -1; // 计数,记录结果长
for (auto item : map) {
if (item.second > count || (item.second == count && item.first < word)) { // 长或 同长和小(C++中second表值,first表键)
word = item.first;
count = item.second;
}
}
cout << word << " " << to_string(count);
return 0;
}
1084 Broken Keyboard
题目:找出破损输出不了的字母,并大写 数字输出(行字符不超过80个)
思路:(哈希表)两个指针遍历,不相同则只存入字符哈希表(字母均转为大写),然后遍历时输出即可按照顺序输出即可
函数:toupper转化为大写字符,static_cast<int>强制装换为INT类型
#include <iostream>
using namespace std;
int main() {
string str1, str2;
cin >> str1 >> str2;
bool s[100] = {0};
str2 += "?"; // 给str2加一个字符,防止越界
for (int i = 0, j = 0; i < str1.size(); i++) {
char a = toupper(str1[i]);
char b = toupper(str2[j]);
if (a == b) j++;
else {
if (!s[a])
cout << a;
s[a] = 1;
}
}
// cout << static_cast<int>('A') << " " << static_cast<int>('0'); # 65 48 static_cast<int>强制装换为Ascii
return 0;
}
1108 Finding Average
关键在于如何判断是否是整数
思路:遍历的时候去判断是否[-1000,1000]内小数点后不超过2位的数,如何累计和和数目
(个人想法,按字符串遍历字符去判断是否满足该数规则)
(用try语句判断是否是正常的浮点数,然后再理性判断是否在访问和小数点是否符合)
函数:stof装换float类型,try catch语句,find找索引位,c_str把std的字符串变成c的字符串(可以给printf输出)
#include<iostream>
using namespace std;
int main() {
int count = 0;
double sum = 0.0;
int n;
cin >> n;
while (n--) {
string num;
cin >> num;
double x;
bool is_legal = true;
try {
// size_t idx; // size_t是无符号整数类型,通常用于表示大小和索引。这里表示位数
// x = stof(num, &idx); // stof将字符串转为float类型,&idx表示变量idx的地址,记录idx字符位数
// if (idx < num.size()) is_legal = false; // 前面正常数字,后面乱来,比如5.20000
x = stof(num); // 直接此情况就可以跑过测试样例
} catch(...) {
is_legal = false; // 异常情况,都是字符或多个小数点
}
// 判断完是否是正常浮点数后,判断是否在[-1000,1000]与小数点后不超过2位
if (x < -1000 || x > 1000) is_legal = false;
int k = num.find("."); // 找不到会返回-1,是索引位
if (k != -1 && num.size() - k > 3) is_legal = false;
if (is_legal) count++, sum+=x;
else printf("ERROR: %s is not a legal number\n", num.c_str()); // c_str()将std::string对象转换为C风格字符串(以空字符'\0'结尾的字符数组)
}
if (count == 0) printf("The average of 0 numbers is Undefined");
else if (count == 1) printf("The average of 1 number is %.2f", sum); // 这里需要靠调测测出,PAT常有这种加不加s语法操作
else printf("The average of %d numbers is %.2f", count, sum / count);
return 0;
}
1124 Raffle for Weibo Followers
M N S 分别代表 转发总量、中奖间隔 第一位中奖者的序号
比如样例1中
Imgonnawin!
PickMe
PickMeMeMeee
就会选PickMe(2号),然后每隔三选取,若选取重复的,则选取下一个没抽过的,再开始按间隔给奖励
实现:哈希表(Set)
思路:存储抽过奖的人入哈希表,然后判断输出即可
(1)实现1,边遍历边操作,同时i走主遍历,k走间隔遍历,符合k或s清空k,否则k++
(2)实现2,遍历和操作分开,先主遍历,然后k做主遍历,符合k+n,不符合k++
#include<iostream>
#include<unordered_set>
using namespace std;
int main() {
int m, n, s;
cin >> m >> n >> s;
unordered_set<string> set;
if (m < s) cout << "Keep going...";
else
for (int i = 0, k = 0; i < m; i++, k++) {
string str;
cin >> str;
if (i == s - 1) { // 首次遍历
set.insert(str);
cout << str << endl;
k = 0;
} else if (i >= s && k == n) { // 后续遍历
if (set.count(str)) k = n - 1; // 是否访问过
else {
set.insert(str);
cout << str << endl;
k = 0;
}
}
}
return 0;
}
1141 PAT Ranking of Institutions
实现:哈希表 + 迭代器
思路:遍历存入hash(name-结构体类型),按规则赋值(因为排序是横向多维的,只能依靠k-v的其中一方)
将value装入vector,并重写operator按规则比较,最后遍历赋值rank,输出
函数:sort,begin,end,tolower,c_str,size
#include<iostream>
#include<unordered_map> // School - AllScore(这里用School的结构体,为了最后排序后还能把其他数据一起读出,如NS)
#include<vector> // 最后的为了排序
#include<algorithm> // 引入sort函数引入的
using namespace std;
struct School {
string name; // School
double sum; // TWS
int count; // NS
School() : sum(0),count(0) {} // 构造函数
bool operator< (const School &t) const {
if (sum != t.sum) return sum > t.sum; // 分数高的靠前
if (count != t.count) return count < t.count; // 分数相同时,人数高的靠前
return name < t.name; // 最后才是名称排序前的靠前
}
};
int main() {
int n;
cin >> n;
unordered_map<string, School> map;
while (n--) {
string id, sch;
double score;
cin >> id >> score >> sch;
// 小写化
for(auto& c : sch) c = tolower(c); // tolower函数只能用在字符上(相当于将sch的字母全部遍历小写)
// 按加分规则加分
if (id[0] == 'B') map[sch].sum += score / 1.5;
else if (id[0] == 'A') map[sch].sum += score;
else map[sch].sum += score * 1.5;
// 入hash表
map[sch].name = sch;
map[sch].count++;
}
vector<School> schs;
for (auto item : map) {
item.second.sum = (int) item.second.sum; // 取整
schs.push_back(item.second); // 将value塞入vector,准备排序
}
sort(schs.begin(), schs.end()); // 排序(begin end分别代表起始迭代器和结束迭代器)
// 输出
cout << schs.size() << endl;
int rank = 1;
for (int i = 0; i < schs.size(); i++) {
auto s = schs[i];
if (s.sum != schs[i - 1].sum) rank = i + 1;
printf("%d %s %d %d\n", rank, s.name.c_str(), (int) s.sum, s.count);
}
return 0;
}
1153 Decode Registration Card of PAT
N M 准考证数 结果需要数
遇1,扫准考证号首字母,按分数降序排,同分按字母序增序排 (意味使用哈希表,同时自建结构体,同时使用vector才能付出两维排序)
遇2,扫准考证号考场号,用一个变量累计和即可,没有对应的考场号输出NA
遇3,扫描准考证号时间,按考场号-对应当天考场号人数 输出,按人数非升序,考场号升序
hash 准考证-结构体(准考证 + 分数)
hash 考场时间-结构体(考场号 + 人数)
情况1可预知:1 A 1 B 1 T 3个hash(准考证-准考证和分数)
情况2可预知:2 考场号 1个hash(考场号-分数和)
情况3可预知:3 时间 (复杂度集中)
函数:
substr取函数位数(索引,长度)
push_back推送
sort(x.begin(), x.end())排序
c_str() 转成字符串
能用printf尽量用,cout比较耗时间
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <algorithm>
using namespace std;
struct rs { // register score
string reg;
int score;
bool operator< (const rs &r) const { // true靠前的原则
if (score != r.score) return score > r.score;
return reg < r.reg; // 成绩相同的情况下再去比较其他的
}
};
struct tn { // test number
string date;
int number;
int score;
tn() : number(0),score(0) {}
};
struct gn { // grade number
string grade;
int number;
gn() : number(0) {}
bool operator< (const gn &g) const {
if (number != g.number) return number > g.number;
return grade < g.grade;
}
};
int main() {
int n, m;
cin >> n >> m;
unordered_map<string, rs> hashA, hashB, hashT;
unordered_set<string> set;
unordered_map<string, tn> hash2;
for (int i = 0; i < n; i++) {
string reg;
int score;
cin >> reg >> score;
set.insert(reg);
char c = reg[0];
if (c == 'A') {
hashA[reg].reg = reg; // hash key结构体赋值直接赋值
hashA[reg].score = score;
} else if (c == 'B') {
hashB[reg].reg = reg;
hashB[reg].score = score;
} else {
hashT[reg].reg = reg;
hashT[reg].score = score;
}
string date = reg.substr(1, 3);
hash2[date].number ++;
hash2[date].score += score;
}
vector<rs> vecA, vecB, vecT;
for (auto a : hashA) vecA.push_back(a.second);
sort(vecA.begin(), vecA.end());
for (auto a : hashB) vecB.push_back(a.second);
sort(vecB.begin(), vecB.end());
for (auto a : hashT) vecT.push_back(a.second);
sort(vecT.begin(), vecT.end());
for (int i = 0; i < m; i++) {
int key;
string str;
cin >> key >> str;
printf("Case %d: %d %s\n", i+1, key, str.c_str());
if (key == 1) {
if (str == "A") {
if(vecA.empty()) printf("NA\n");
for (auto a : vecA) printf("%s %d\n", a.reg.c_str(), a.score);
} else if (str == "B") {
if(vecB.empty()) printf("NA\n");
for (auto a : vecB) printf("%s %d\n", a.reg.c_str(), a.score);
} else {
if(vecT.empty()) printf("NA\n");
for (auto a : vecT) printf("%s %d\n", a.reg.c_str(), a.score);
}
} else if(key == 2) {
if (hash2.find(str) == hash2.end())
printf("NA\n");
else
printf("%d %d\n", hash2[str].number, hash2[str].score);
} else {
unordered_map<string, gn> hash;
bool bo = false;
for (auto a : set)
if (a.substr(4, 6) == str) {
string key3 = a.substr(1, 3);
hash[key3].number ++;
hash[key3].grade = key3;
bo = true;
}
if (bo) {
vector<gn> vec;
for (auto a : hash) vec.push_back(a.second);
sort(vec.begin(), vec.end());
for (auto a : vec) printf("%s %d\n", a.grade.c_str(), a.number);
} else {
printf("NA\n");
}
}
}
return 0;
}
1058 A+B in Hogwarts
Galleon is an integer in [0,10^7], Sickle is an integer in [0, 17), and Knut is an integer in [0, 29)
比如 Sickle到18 那么就会进位到 Galleon
接收函数:scanf
#include <iostream>
using namespace std;
int main() {
int a, b, c, d, e, f;
scanf("%d.%d.%d %d.%d.%d", &a, &b, &c, &d, &e, &f); // 接受类似 3.2.1 10.16.27
// 相加
a += d;
b += e;
c += f;
// 进位
b += c / 29;
c %= 29;
a += b / 17;
b %= 17;
printf("%d.%d.%d", a, b, c);
}
1136 A Delayed Palindrome
题型:模拟题
题目:数(不超过1千位,说明不能用数字接收,那么反向相加得用一些数组之类的方法),注意自身也可能为回文数
数 + 数的反序,若为回文返回,不会回文继续如此操作,直到找到回文或者操作满10次为止。
思路:用动态数组接受,反向入位(数位越小的索引越小,最好遇到这类题都这么干,方便定位进位),然后计数,到10次则出或者已经找到回文数出(读反向回文-vector计算-读和)
函数: puts to_string把数字变字符 c_str字符串输出
#include <iostream>
#include <vector> // 动态大小的数组
using namespace std;
bool check(vector<int> a) { // 判断是否为回文数
for (int i = 0; i < a.size() / 2; i++) {
if (a[i] != a[a.size() - 1 - i]) {
return false;
}
}
return true;
}
int main() {
string str;
cin >> str;
vector<int> a;
// 反向入库,更符合逻辑一点,比如123 对应上索引依次为 3 2 1,也能便于往后进位
for (int i = str.size() - 1; i >= 0; i--) a.push_back(str[i] - '0'); // 2 5 1 7 9
int count = -1; // 计数器
while (++count < 10) { // 这里++放前面是为了控制第十位成功的情况
if (check(a)) break; // 输入数为回文数也算
string s1 = str;
string s2;
// bool bo = false; // 相反的非0首位 // if (!bo and a[i] != '0') bo = true; // if (bo) // 考虑多了,不需要剔除0
for (int i = 0; i < str.size(); i++) s2 += to_string(a[i]); // 25179
// 实施加法
for (int i = 0; i < s2.size(); i++) {
a[i] += s2[s2.size() - i - 1] - '0'; // 低位相加,这里的a[i]一定则定
int j = i;
while (a[j] >= 10) {
a[j++] -= 10;
if (j >= a.size()) a.push_back(1);
else a[j] += 1;
}
}
str = "";
// 记录
for (int i = a.size() - 1; i >= 0; i--) str += to_string(a[i]); // 133221
printf("%s + %s = %s\n", s1.c_str(), s2.c_str(), str.c_str());
}
if (count == 10) {
puts("Not found in 10 iterations.");
} else {
printf("%s is a palindromic number.\n", str.c_str());
}
return 0;
}
1098 Insertion or Heap Sort
题意:第二行是原数组,第三行是某次排序数组后的结果,判断是哪种排序(结果唯一),然后输出下次排序的结果
考察:对插入排序和堆排序的了解程度
思路:因为结果唯一是,所以只要能判断是哪一种或不是哪一种,那么结果就迎刃而解了(这里可以采取匹配最简单的排序,插入排序匹配会简单一些)。
施加排序,看是否有相等的一次,没有说明是另一种排序,匹配后输出下一次排序结果即可
对插入排序有更简单的判断方法,根据插入排序的形式,扫到第k位,那么前k位一定是排好序的,后n-k位与原本相同——稳定
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 101;
int n;
int a[N], b[N];
void heap(int i, int len) { // 这里本需参数a,但已经全局了,就不必要了
int val = a[i]; // 准备替换
for (int k = 2 * i + 1; k < len; k = 2 * k + 1) { // 每次都往下一个叶子节点跳
if (k + 1 < len && a[k] < a[k + 1]) k++; // 左右节点取值最大的那个节点
if (a[k] > val) {
a[i] = a[k]; // 替换
i = k; // 记录下移索引
} else {
break; // 节省时间,因为在外层我们遍历heap时,子树就已经是有序的了,后面不可能会被替换
}
}
a[i] = val;
}
bool check() { // 判断a与b是否相等 // 同理
for (int i = 0; i < n; i++) if (a[i] != b[i]) return false;
return true;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> b[i];
// 检查是否是插入排序
int k = 1;
while (b[k] >= b[k - 1]) k++; // 因为不可能是已经排好序的,所以不必要做长度拦截
bool bo = true;
for (int i = k; i < n; i++) {
if (a[i] != b[i]) {
bo = false;
break;
}
}
if (bo) { // 符合则是插入排序
puts("Insertion Sort");
sort(b, b + k + 1); // C++中数组地址是首元素地址,而sort的end是要排序的最后一个位置的下一个位置,这里要排到b+k
printf("%d", b[0]);
for (int i = 1; i < n; i++) printf(" %d", b[i]);
} else {
puts("Heap Sort");
// 接下来的做法是修改a做堆排序,找到a与b相同的顺序,然后获取下一个排序即可
for (int i = n / 2 - 1; i >= 0; i--) heap(i, n); // 首次构成大顶推(这个必须单独的,而且必须底到顶,避免有些子树小值在上)
int j = n - 1;
while (true) {
bool bo = check();
// 交换
int temp = a[0];
a[0] = a[j];
a[j] = temp;
heap(0, j--);
if (bo) break;
}
printf("%d", a[0]);
for (int i = 1; i < n; i++) printf(" %d", a[i]);
}
return 0;
}
1089 Insert or Merge
与上题同理,只是堆排序被换成归并排序
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 101;
int n;
int a[N], b[N];
bool check() { // 判断a与b是否相等 // 同理
for (int i = 0; i < n; i++) if (a[i] != b[i]) return false;
return true;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> b[i];
// 检查是否是插入排序
int k = 0;
while (b[k + 1] >= b[k]) k++; // 因为不可能是已经排好序的,所以不必要做长度拦截,注意等于号
bool bo = true;
for (int i = k + 1; i < n; i++) {
if (a[i] != b[i]) {
bo = false;
break;
}
}
if (bo) { // 符合则是插入排序
puts("Insertion Sort");
sort(b, b + k + 2); // C++中数组地址是首元素地址,而sort的end是要排序的最后一个位置的下一个位置,这里要排到b+k
printf("%d", b[0]);
for (int i = 1; i < n; i++) printf(" %d", b[i]);
} else {
puts("Merge Sort");
// 接下来的做法是修改a做归并排序,找到a与b相同的顺序,然后获取下一个排序即可
int len = 1;
while (true) {
bool match = check(); // 在里面算,为了就是记录为true的下一次能一起出来
len *= 2;
for (int i = 0; i < n; i += len) sort(a + i, a + min(n, i + len));
if (match) break;
}
printf("%d", a[0]);
for (int i = 1; i < n; i++) printf(" %d", a[i]);
}
return 0;
}
1018 Public Bike Management
考点:最短路 + 深搜
(1)距离最短
(2)最初带去的数量越少越好
(3)最终带回的数量越少越好
思路:用dijsktra搜出最短路径后,暴力搜一遍过程带的车,里面的最小值就是答案,最小值
#include <iostream>
#include <vector>
#include <cstring> // 才可用memset
using namespace std;
const int N = 501, INF = 0x3f3f3f3f;
int C, n, S, m; // 站点最大容量,站点数,问题站(终点站),道路数(路径数)
int stop[N]; // 起始时每个点的停车数量
int g[N][N]; // 记录站点距离
int dist[N]; // 记录某点到某点过程的最短距离
bool visit[N]; // dijsktra的记忆数组
vector<int> path, ans; // 存储路径和最优值
int send = INF, bring = INF; // 赋值正无穷
void dijkstra() {
memset(dist, 0x3f, sizeof dist); // 初始为正无穷
dist[S] = 0; // 每个点到S的距离,故必须要初始化S
for (int i = 0; i < n; i++) {
int j = -1;
for (int k = 0; k <= n; k++)
if (!visit[k] && (j == -1 || dist[k] < dist[j])) j = k; // 最小的dist,开始时,最小的dist即为终点
visit[j] = true;
for (int k = 0; k <= n; k++) {
dist[k] = min(dist[k], dist[j] + g[j][k]);
}
}
}
void dfs(int u, int s, int mins) {
if (u) { // 除了起点0(起点不需要补自行车)
s -= (C + 1) / 2 - stop[u]; // 缺的数
mins = min(mins, s); // 记录最小数
}
if (u == S) { // 走到终点
int sd = abs(min(mins, 0)); // 正数为0,负数为自己
int bg = s + sd; // 缺的数 + 最小数 = 初始带的数
if (sd < send) ans = path, send = sd, bring = bg; // 查找最少带出
else if (sd == send && bg < bring) ans = path, bring = bg; // 查找最少带回
return;
}
for (int i = 1; i <= n; i++) {
if (dist[u] == g[u][i] + dist[i]) { // 如果是最短路径
path.push_back(i);
dfs(i, s, mins);
path.pop_back();
}
}
}
int main() {
cin >> C >> n >> S >> m;
for (int i = 1; i <= n; i++) cin >> stop[i];
memset(g, 0x3f, sizeof g); // 初始化为 正无穷
for (int i = 0; i < m; i++) {
int x, y, z;
cin >> x >> y >> z;
g[x][y] = g[y][x] = min(g[x][y], z);
}
dijkstra();
path.push_back(0);
dfs(0, 0, 0);
cout << send << ' ' << 0;
for (int i = 1; i < ans.size(); i++)
cout << "->" << ans[i];
cout << " " << bring << endl;
return 0;
}
1072 Gas Station
dist <= D(都要在范围内)
最小的dist最大
dist平均数越大 等价于 总和越大
做dijkstra
#include <iostream>
#include <cstring> // 才可用memset
using namespace std;
const int N = 1020, INF = 0x3f3f3f3f;
int n, m, K, D; // 房屋总数,加油站候选位置数, 连接房屋或加油站的道路总数 加油站的最大服务范围
int g[N][N];
int dist[N];
bool visit[N];
int get(string s) {
if (s[0] == 'G') return n + stoi(s.substr(1)); // 如果第一个字符是 'G',则从字符串的第二个字符开始提取子字符串,并将其转换为整数。然后,返回该整数值 + n (防与房屋重复)。
return stoi(s); // 如果第一个字符不是 'G',则直接将整个字符串转换为整数,并返回该整数值。
}
void dijkstra(int start, int &mind, int &sumd) {
memset(dist, 0x3f, sizeof dist);
memset(visit,0,sizeof visit);//因为枚举,所以每次都有进行初始化
dist[start] = 0;
for (int i = 0; i < n + m; i++) {
int t = -1;
for (int j = 1; j <= n + m; j++)
if (!visit[j] && (t == -1 || dist[j] < dist[t])) t = j;
visit[t] = true;
for (int j = 1; j <= n + m; j++)
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
for (int i = 1; i <= n; i++)
if (dist[i] > D) {
mind = -1;
return;
}
mind = INF, sumd = 0;
for (int i = 1; i <= n; i++) {
mind = min(mind, dist[i]);
sumd += dist[i];
}
}
int main() {
cin >> n >> m >> K >> D;
memset(g, 0x3f, sizeof g);
while (K--) {
string a, b;
int z;
cin >> a >> b >> z;
int x = get(a), y = get(b);
g[x][y] = g[y][x] = min(g[x][y], z); // 防止重复路径
}
int res = -1, mind = 0, sumd = INF;
for (int i = n + 1; i <= n + m; i++) {
int d1, d2;
dijkstra(i, d1, d2);
if (d1 > mind) res = i, mind = d1, sumd = d2;
else if (d1 == mind && d2 < sumd) res = i, sumd = d2;
}
if (res == -1) puts("No Solution");
else printf("G%d\n%.1lf %.1lf", res-n, (double)mind, (double)sumd/n+1e-8);
return 0;
}