洛谷 P3405 [USACO16DEC] Cities and States S(详解)c++

embedded/2025/2/25 3:32:08/

题目链接:P3405 [USACO16DEC] Cities and States S - 洛谷

1.分析题目

  • 对于一个城市,我只考虑前两个字符是什么,剩下的字符我不关心,对这道题没有影响

2.算法原理 

对于形如 <ab,xy>这样的对应关系,找出有多少个形如 <xy,ab>这样的对应关系的城市;
也就是从前往后遍历,在每一个城市里都可以找到<ab,xy>这样的对应关系,在看看在所有的城市里面有多少个城市和州是<xy,ab>这样的对应关系

如何把<ab,xy>/<xy,ab>这样的对应关系存下来


因为它是一个字符串对应另一个字符串的关系,我还要统计这样的对应关系多少个,直接存不太好存,我们可以把对应关系简化一下,针对xy__城市可以把前两个字符给摘出来,和后面州名字拼接起来,xyab,意思就是xy对应ab,此时统计这个字符串出现次数的时候,就特别的好统计,因此待会儿存的时候,不存ab->xy这样的东西,是把ab和xy拼接成一个字符串存起来,往后就很好统计了,直接用哈希表统计xyab字符串出现多少个就可以了,用哈希表统计<拼接后的对应关系,次数>

题目说特殊城市要来自不同的州,如何确定是不同的州


假设我们要找的城市属于同一个州,会有什么问题,两个城市开头两个字母都是ab,对应的州也是ab,根据交叉原则,第二个城市对应的州ab可以转移到第一个城市ab,第一个州的ab是可以转移到第二个城市的ab,也就是说,如果这两个城市属于同一个州,它们前两个字母是和州名字是一样的,此时是不能统计这种情况的,它就不属于特殊的州了,所以一会儿统计到像这样的对应关系的时候,要跳过

另一种思路可以用哈希表嵌套哈希表的形式把对应表关系给存下来,但这样的写法有点不太舒服,所以我们用第一种思路就好了,感兴趣可以往下翻翻看

代码:

#include <iostream>
#include <unordered_map>
using namespace std;int n;int main()
{cin >> n;unordered_map<string, int> mp; // <拼接后的对应关系,次数>int ret = 0;while (n--){string a, b; cin >> a >> b;a = a.substr(0, 2);if (a == b) continue; // 往后查找的时候属于都一个州ret += mp[b + a]; // 统计 b->a 一共有多少个mp[a + b]++;  //多了一组a拼接b的形式,在之前的基础上++}cout << ret << endl;return 0;
}

代码(嵌套unordered_map):

#include <iostream>
#include <unordered_map>
using namespace std;int n;int main()
{cin >> n;unordered_map<string, unordered_map<string, int>> mp; // <拼接后的对应关系,次数>int ret = 0;while (n--){string a, b; cin >> a >> b;a = a.substr(0, 2);if (a == b) continue; // 往后查找的时候属于都一个州ret += mp[a][b]; // 统计 b->a 一共有多少个mp[b][a]++;  //多了一组a拼接b的形式,在之前的基础上++}cout << ret << endl;return 0;
}

代码(pair):

1.为什么需要自定义哈希函数?

在 C++ 中,unordered_map 的键必须满足两个条件:

  • 可哈希:必须能通过哈希函数转换为 size_t 类型的值。
  • 可比较相等:必须支持 operator== 比较。

标准库已经为 pair 类型定义了 operator==,但没有提供默认的哈希函数。因此,当你想用 pair<string, string> 作为 unordered_map 的键时,必须手动定义哈希函数

2.用异或算哈希值的缺陷

struct PairHash {size_t operator()(const pair<string, string>& p) const {return hash<string>()(p.first) ^ hash<string>()(p.second);}
};

异或的对称性(a ^ b = b ^ a)会导致以下问题

pair<string, string> p1("A", "B");
pair<string, string> p2("B", "A");

它们的哈希值将完全相同:
hash(p1.first) ^ hash(p1.second) = hash(p2.first) ^ hash(p2.second)
即使 p1 和 p2 是不同的键,也会被映射到相同的哈希值,导致哈希冲突 

方法:位移 + 异或;通过位移破坏对称性,确保顺序不同的键生成不同的哈希值

struct PairHash {size_t operator()(const pair<string, string>& p) const {return (hash<string>()(p.first) << 16) ^ hash<string>()(p.second);}
};
  • 位移位数需根据哈希值长度调整(例如 64 位系统用 << 32) 
#include <iostream>
#include <unordered_map>
#include <string>
using namespace std;// 定义 pair<string, string> 的哈希函数
//在哈希函数中必须返回 size_t,以满足 unordered_map 或 unordered_set 的接口要求
struct PairHash {size_t operator()(const pair<string, string>& p) const {//先通过`hash<string>()`创建一个临时函数对象,然后通过`(p.first)`来调用这个对象的                        //`operator()`方法,从而计算哈希值;果写成`hash<string>(p.first)`,这会被解析为试图用 //`p.first`作为构造函数的参数,但`std::hash`的构造函数并不接受这样的参数        return (hash<string>()(p.first) << 16) ^ hash<string>()(p.second);}
};int main() {int n;cin >> n;unordered_map<pair<string, string>, int, PairHash> mp; // 键为州代码 + 城市代码的组合int ans = 0;for (int i = 0; i < n; ++i) {string city, state;cin >> city >> state;string city_code = city.substr(0, 2);if (state == city_code) continue;// 查找是否存在对应关系 (city_code, state) 的键ans += mp[{city_code, state}];// 将 (state, city_code) 的组合记录到哈希表中mp[{state, city_code}]++;}cout << ans << endl;return 0;
}

拓展:

为什么nts1 和 nts2 的哈希比较结果为 false

C++ 标准库为指针类型(如 char*)的哈希函数设计为直接对指针的内存地址进行哈希,而不是对其指向的内容。即使两个指针指向的内容相同(如 "Test"),只要它们的地址不同,哈希值就不同。nts1 和 nts2 是独立的字符数组,虽然内容相同,但内存地址不同。最终结果为 false

C++ 标准库为 std::string 的哈希函数设计为对其字符串内容进行哈希。只要两个字符串内容相同,哈希值一定相同,str1 和 str2 的内容完全相同,最终结果为 true

#include <iostream>
#include <functional>
#include <string>
#include <cstring>
using namespace std;int main() {char nts1[] = "Test";char nts2[] = "Test";std::string str1(nts1);std::string str2(nts2);cout << strcmp(nts1, nts2) << endl;cout << (str1 == str2) << endl;std::hash<char*> ptr_hash;std::hash<std::string> str_hash;// 打印地址std::cout << "nts1 地址: " << (void*)nts1 << std::endl;std::cout << "nts2 地址: " << (void*)nts2 << std::endl;// 打印哈希值std::cout << "nts1 哈希: " << ptr_hash(nts1) << std::endl;std::cout << "nts2 哈希: " << ptr_hash(nts2) << std::endl;std::cout << "str1 哈希: " << str_hash(str1) << std::endl;std::cout << "str2 哈希: " << str_hash(str2) << std::endl;return 0;
}//0
//1
//nts1 地址 : 006AF950
//nts2 地址 : 006AF940
//nts1 哈希 : 1115828456
//nts2 哈希 : 3594615800
//str1 哈希 : 805092869
//str2 哈希 : 805092869


http://www.ppmy.cn/embedded/164945.html

相关文章

Java并发编程——ThreadLocal

文章目录 一、ThreadLocal 基本概念二、ThreadLocal 的数据结构三、GC 之后 ThreadLocal 的 key 是否为 null&#xff1f;3.1 Java 的四种引用类型3.2 ThreadLocal 的 key 是弱引用3.3 代码演示&#xff1a;GC 后 key 的状态3.4 关键点分析3.5 内存泄漏问题 四、ThreadLocal.se…

【MySQL篇】数据库基础

目录 1&#xff0c;什么是数据库&#xff1f; 2&#xff0c;主流数据库 3&#xff0c;MySQL介绍 1&#xff0c;MySQL架构 2&#xff0c;SQL分类 3&#xff0c;MySQL存储引擎 1&#xff0c;什么是数据库&#xff1f; 数据库&#xff08;Database&#xff0c;简称DB&#xf…

百度首页上线 DeepSeek 入口,免费使用

大家好&#xff0c;我是小悟。 百度首页正式上线了 DeepSeek 入口&#xff0c;这一重磅消息瞬间在技术圈掀起了惊涛骇浪&#xff0c;各大平台都被刷爆了屏。 百度这次可太给力了&#xff0c;PC 端开放仅 1 小时&#xff0c;就有超千万人涌入体验。这速度&#xff0c;简直比火…

力扣hot100 ——搜索二维矩阵 || m+n复杂度优化解法

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 解题思路&#xff1a; 借助行和列有序特性&#xff0c;不断按行或者列缩小范围&#xff1b;途中数字表示每…

k8s Container runtime network not ready

问题 k8s 3 控制节点,docker 运行时,后期踢掉其中一个节点,使用了 containerd 运行时,但是在加入集群的时候,node 状态 notready。查看 kubelet 的日志发现如下报错 Feb 20 11:28:14 bjm3 kubelet[144781]: E0220 11:28:14.506374 144781 kubelet.go:2475] "Conta…

23种设计模式之《桥接模式(Bridge)》在c#中的应用及理解

程序设计中的主要设计模式通常分为三大类&#xff0c;共23种&#xff1a; 1. 创建型模式&#xff08;Creational Patterns&#xff09; 单例模式&#xff08;Singleton&#xff09;&#xff1a;确保一个类只有一个实例&#xff0c;并提供全局访问点。 工厂方法模式&#xff0…

设计模式相关知识点

目录 设计模式 设计模式 代码设计原则 设计模式 设计模式 干掉if...else&#xff0c;最好用的3种设计模式&#xff01; | 小傅哥 bugstack 虫洞栈 代码设计原则-CSDN博客 23种设计模式-CSDN博客 策略模式&#xff08;Strategy Pattern&#xff09;-CSDN博客 责任链模式…

C从入门到放弃篇1

各位新入坑C语言的朋友&#xff0c;你们有福了因为你们遇到了我&#xff0c;我会带你放弃C语言&#xff0c;哈哈哈哈哈。 其实&#xff0c;学任何东西都是循序渐进的&#xff0c;在学习的初期投入更多的精力&#xff0c;将来你会越学越快。我相信&#xff0c;放弃是最容易的事…