题意:
给你一个的排列和个数对,每对数对表示可以将和两个元素交换,你可以交换任意多次,问任意操作次后能得到的字典序最小的排列是什么?
解法一(BFS,不可AC)
用BFS枚举所有可能的排列,最后取字典序最小的排列
判重可以将排列转换成字符串再用unordered_map进行判重
具体的:
1. 刚开始往队列中push初始排列,并且标记访问过
2. 当队列非空时一直循环
3. 取出队头的排列,若当前排列比答案更优,则替换答案,枚举所有一次交换后会变成的排列,判重后再push到队列中,并且标记该排列访问过
4. 重复步骤2
代码:
class Solution { public: string p_to_string(vector<int>& p){ //将排列转换成字符串,便于使用unordered_map string ret(p.size(),' '); for(int i=0;i<p.size();i++){ ret[i]=p[i]; } return ret; } vector<int> string_to_p(string& s){ //将字符串转换为排列 vector<int> ret(s.size()); for(int i=0;i<s.size();i++){ ret[i]=s[i]; } return ret; } vector<int> solve(int n, int m, vector<int>& perm, vector<Point>& Pair) { unordered_map<string,bool> vis;//用于判重 queue<string> que;//bfs所用队列 string st=p_to_string(perm); que.push(st);//将初始排列push入队列 vis[st]=true; string ans=st; while(!que.empty()){ string t=que.front();//取出队头 que.pop(); if(t<ans){ ans=t;//更新答案 } for(auto p:Pair){//枚举所有一次交换后的情况 string ex=t; swap(ex[p.x-1],ex[p.y-1]); if(!vis[ex]){ que.push(ex); vis[ex]=true; } } } return string_to_p(ans);//返回答案 } };时间复杂度:,最坏情况有排列方案,故时间复杂度为
空间复杂度:,最坏情况有排列方案,需要存储个排列,故空间复杂度为
解法二(并查集)
我们考虑将个数对看成条无向边,然后连成若干个联通图
以本题样例来说:
其中是联通的,我们发现由于可以交换任意多次,故在同一个联通图内元素的顺序我们可以任意指定
我们先将同一个连通块内的下标标红
然后再将同一个连通块内的元素排序
这样我们就得到本题样例最优的答案了
那么我们只需要对若干个联通图内的元素进行从小到大排序,最后拼接起来的一定是字典序最小的排列
具体的判断连通性我们可以使用并查集快速解决
代码:
/** * struct Point { * int x; * int y; * }; */ class Solution { public: int fa[100001];//并查集父节点数组 int find_root(int x){//并查集寻找根节点 if(fa[x]==x)return x; return fa[x]=find_root(fa[x]);//路径压缩 } struct node{ int idx,val;//idx为对应在排列中的下标,val为对应元素值 inline node(int idx,int val):idx(idx),val(val){} inline bool operator < (const node& other)const{ return val<other.val;//按照元素大小排序 } }; vector<node> datas[100001];//存储不同连通块内的元素 vector<int> solve(int n, int m, vector<int>& perm, vector<Point>& Pair) { for(int i=1;i<=n;i++){ fa[i]=i;//并查集初始化 } for(int i=1;i<=n;i++){ datas[i].clear();//多测清空 } for(auto p:Pair){ //构造联通图 int x=p.x,y=p.y; if(find_root(x)==find_root(y))continue; //合并 fa[find_root(x)]=find_root(y); } for(int i=1;i<=n;i++){ //将元素放到对应的连通块内 datas[find_root(i)].push_back(node(i-1,perm[i-1])); } for(int i=1;i<=n;i++){ //不同连通块内进行排序 sort(datas[i].begin(),datas[i].end()); } for(int i=1;i<=n;i++){ vector<int> idxs;//存储不同连通块内的元素在排列中原本的下标 for(auto x:datas[i]){ idxs.push_back(x.idx); } sort(idxs.begin(),idxs.end());//下标从小到大排序 for(int j=0;j<idxs.size();j++){ perm[idxs[j]]=datas[i][j].val;//将已经排好序的元素依次放入排列中 } } return perm;//返回答案 } };时间复杂度:,对元素排序的复杂度是的,路径压缩并查集复杂度是近似的,故总的时间复杂度为