前言

如果你对这篇文章可感兴趣,可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」,查看完整博客分类与对应链接。

问题概述:

最近频繁遇见图上的第 kkk 小问题,比如无向图的第 kkk 小团、有向图上的第 kkk 小路径,无向图上的第 kkk 小最短路问题。

然后这类方法的解决方法都比较相近,主要思考方向是先将边或单点加入堆中,然后每次弹出一个最小值,再用最小值去更新到一个新的状态,并将新状态加入到堆中,一直到计算到 kkk 次为止。

主要思想就是这样,但是具体操作上,对于不同的问题,涉及到不同的处理方式,主要就是关于算重和算漏的问题。如何不重不漏地计算成为此类问题的关键。


1. 无向图第k小团

题意: 给出一个 nnn 个点的无向图,每个点都有一个权重,求出图中第 kkk 小的团,找不到输出 −1-1−1。团的定义为完全图。(1≤n≤100,1≤k≤106)(1\leq n\leq 100,1\leq k\leq 10^6)(1≤n≤100,1≤k≤106)

思路: 此类的问题的思路比较统一,先定一个状态,然后再将这个状态丢入堆中,去更新其它状态。

我们定状态为团的权重,并用 bitsetbitsetbitset 记录团中所有出现的点,并记录每个团中编号最大的节点。

我们更新时每次选一个编号比当前团中任意节点都大的一个节点加入团中。判断一个点是否可以加入团,只需判断这个点的邻接矩阵和记录团中点的 bitsetbitsetbitset 进行与运算,如果最终结果仍然是团中点的 bitsetbitsetbitset,则证明当前点和团中所有点都有连边,因此加入这个点仍可以构成一个团。

总结: 此处运用 bitsetbitsetbitset 去存储团的状态,以及判断点能否加入一个团中。每次往团中加入的点必须比原有点的编号都要大,由此避免重复计算。

代码:

#include <bits/stdc++.h>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 100+10;
const db EPS = 1e-9;
using namespace std;char s[N];
int n,k;
ll w[N];
bitset<105> mp[N];
struct Node{ll w; int id;bitset<105> vis;Node() {w = id = 0;vis.reset();}bool operator < (Node xx) const {return w > xx.w;}
};priority_queue<Node> q;int main()
{scanf("%d%d",&n,&k);rep(i,1,n) scanf("%lld",&w[i]);rep(i,1,n){scanf("%s",s+1);rep(j,1,n)if(s[j] == '1') mp[i].set(j);}while(q.size()) q.pop();Node empty; ll ans = 0;q.push(empty);while(q.size()){Node x = q.top(); q.pop();k--;if(k == 0) {ans = x.w; break;}rep(i,x.id+1,n)if(x.vis[i] == 0 && (x.vis&mp[i]) == x.vis){Node nw = x;nw.w += w[i];nw.vis[i] = 1;nw.id = i;q.push(nw);}}if(k != 0) printf("-1\n");else printf("%lld\n",ans);return 0;
}

2. 有向图第k小路径

题意: 给出一个 nnn 个点 mmm 条边的有向图,每条边都有一个权重。一共有 qqq 组询问,每次询问 kkk,表示询问图中第 kkk 小的路径。(1≤n,m,q,k≤5∗104)(1\leq n,m,q,k\leq 5*10^4)(1≤n,m,q,k≤5∗104)

思路: qqq 组询问,我们离线下来,只用求出最大的那个 kkk 的答案即可。

然后一样的套路,定状态。一条路径的表示,无非是起点和终点,(u,v)(u,v)(u,v),但是如果这样定状态的话,会发现不好更新,难道更新 vvv 的所有出边?但更新 vvv 的所有出边很容易 TTT 。

因此我们令 uuu 为 vvv 的前驱,状态定义为 (u,v,id,w)(u,v,id,w)(u,v,id,w) 表示 uuu 是 vvv 的前驱,且 vvv 是 uuu 的第 ididid 条出边,当前路径权重为 www。

然后每次更新 uuu 的第 id+1id+1id+1 条出边,或者更新 vvv 的第一条出边即可,记得将每个点的出边排序。

代码:

#include <bits/stdc++.h>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const db EPS = 1e-9;
const int N = 5*1e4+10;
using namespace std;int n,m,qn,Q[N],maxn;
ll ans[N];
vector<pair<ll,int> > v[N];
struct Node{int u,v,id;ll w;Node() {}Node(int x,int y,int d,ll z):u(x),v(y),id(d),w(z) {}bool operator < (Node xx) const {return w > xx.w;}
};priority_queue<Node> q;void solve(){while(q.size()) q.pop();int ct = 0;rep(i,1,n) sort(v[i].begin(),v[i].end());rep(i,1,n){if(v[i].size())q.push({i,v[i][0].second,0,v[i][0].first});}while(q.size()){Node x = q.top(); q.pop();ct++;ans[ct] = x.w;if(ct == maxn) break;if(v[x.v].size()){q.push({x.v,v[x.v][0].second,0,x.w+(v[x.v][0].first)});}if((int)v[x.u].size() > (int)(x.id+1)){q.push({x.u,v[x.u][x.id+1].second,x.id+1,x.w-v[x.u][x.id].first+v[x.u][x.id+1].first});}   }rep(i,1,qn){printf("%lld\n",ans[Q[i]]);}
}int main()
{int _; scanf("%d",&_);while(_--){scanf("%d%d%d",&n,&m,&qn);rep(i,0,n) v[i].clear();rep(i,1,m){int x,y; ll w; scanf("%d%d%lld",&x,&y,&w);v[x].push_back(make_pair(w,y));}maxn = 0;rep(i,1,qn){scanf("%d",&Q[i]);maxn = max(maxn,Q[i]);}solve();}return 0;
}

3. 无向图的第k小最短路

题意: nnn 个点,mmm 条边的有权无向图,询问图中第 kkk 大的最短路,注意路径 (u,v)(u,v)(u,v) 被计算,仅当这条路径是 (u,v)(u,v)(u,v) 之间的最短路,保证有解。(2≤n,m≤2∗105,1≤k≤400)(2\leq n,m\leq 2*10^5,1\leq k\leq 400)(2≤n,m≤2∗105,1≤k≤400)

思路: 无向图中的问题,主要难点在于如何才能避免算重。

我们注意到此题是两点之间的最短路,而两点之间只会有一条最短路,路径端点相同且长度相同算一条。因此我们将路径两点存入 mapmapmap,当且仅当路径两点第一次在一条路径中出现时,才更新答案。

因此状态就是路径的起点和终点以及路径的权重,每次选择终点的出边进行状态更新即可。

代码:

#include <bits/stdc++.h>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 2*1e5+100;
const int M = 4*1e5+100;
const db EPS = 1e-9;
using namespace std;int n,m,k;
vector<pair<ll,int> > v[N];
struct Node{ll w; int u,v;Node(ll a,int b,int c) : w(a), u(b), v(c) {}bool operator < (Node xx) const {return w > xx.w;}
};
map<pair<int,int>,int> mp;
vector<Node> temp;
priority_queue<Node> q;void dijsktra(){int cnt = 0;mp.clear();while(q.size()) q.pop();rep(i,1,n) q.push({0,i,i});while(q.size()){Node tp = q.top(); q.pop();if(mp.find(make_pair(tp.u,tp.v)) != mp.end()) continue;if(tp.u != tp.v && mp.find(make_pair(tp.u,tp.v)) == mp.end() && mp.find(make_pair(tp.v,tp.u)) == mp.end()){cnt++;if(cnt == k){printf("%lld\n",tp.w);return;}}mp[make_pair(tp.u,tp.v)] = 1;int rt = 0;for(pair<ll,int> &thp:v[tp.v]){rt++;if(rt+cnt > k) break;int y = thp.second;if(mp.find(make_pair(tp.u,y)) != mp.end()) continue;q.push({tp.w+thp.first,tp.u,y});}}
}int main()
{scanf("%d%d%d",&n,&m,&k);rep(i,1,m){int x,y; ll w; scanf("%d%d%lld",&x,&y,&w);temp.push_back({w,y,x});}sort(temp.begin(),temp.end());int siz = temp.size();for(int i = siz-1; i >= max(siz-1-k,0); i--){int x = temp[i].u, y = temp[i].v, w = temp[i].w;v[x].push_back(make_pair(w,y));v[y].push_back(make_pair(w,x));}rep(i,1,n) sort(v[i].begin(),v[i].end());dijsktra();return 0;
}

4. 无向图的第k小路径

题意: 给出一个 nnn 个点,mmm 条边的一个图,询问图中第 kkk 小的路径。由于并没有遇到过这样的题,只能自行 YY\text{YY}YY,因此没有数据范围。

思路: 无向图的第 kkk 小路径,最大的问题就在于很容易计算重复。那么如何定状态才能避免算重呢?

第一个思路是对于路径 (u,v)(u,v)(u,v),仅当 v>uv>uv>u 时才更新答案。这样可以对于所有非环路径进行一个去重,避免计算重复。

第二个思路就是如何解决环的问题。我们对一条路径经过的所有边进行一个 hashhashhash,将 hashhashhash 值和路径权值一起存入 mapmapmap,对于路径 (u,v)(u,v)(u,v),当 u==vu==vu==v 时,我们判断当前这条路径的 hashhashhash 值和路径总权值是否在 mapmapmap 中出现过,如果是第一次出现则记录答案。

不过这只是一个口头思路,正确性我也不会证明,只是我目前为止还没有找到反例去 hackhackhack 这种做法。如果大家遇到过这个问题请在评论区留下题号,或者对这个做法有任何 hackhackhack 思路的话,都可以在评论区表达自己的想法,谢谢大家。

全图中第K小路径/团问题(有向/无向)相关推荐

  1. 174. 地下城游戏;剑指 Offer 40. 最小的k个数;378. 有序矩阵中第K小的元素;703. 数据流中的第K大元素

    一些恶魔抓住了公主(P)并将她关在了地下城的右下角.地下城是由 M x N 个房间组成的二维网格.我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主. 骑士的初始 ...

  2. LeetCode 230. Kth Smallest Element in a BST--C++,Python解法--面试真题--找二叉树中第K小的元素

    题目地址:Kth Smallest Element in a BST - LeetCode Given a binary search tree, write a function kthSmalle ...

  3. LeetCode每日训练2—有序矩阵中第K小的元素(7.2)

    题目描述 给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素. 请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素. 示例: matrix = [ [ ...

  4. 378. Kth Smallest Element in a Sorted Matrix 有序矩阵中第K小的元素

    Title 给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素. 请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素. 示例: matrix = [ [ ...

  5. LeetCode 668. 乘法表中第k小的数(二分查找)

    文章目录 1. 题目 2. 解题 1. 题目 几乎每一个人都用 乘法表.但是你能在乘法表中快速找到第k小的数字吗? 给定高度m .宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k ...

  6. LeetCode 378. 有序矩阵中第K小的元素(二分查找)

    文章目录 1. 题目 2. 解题 2.1 暴力法 2.2 二分查找 1. 题目 给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素. 请注意,它是排序后的第k小元素,而 ...

  7. 378. 有序矩阵中第K小的元素

    2020-05-25 1.题目描述 有序矩阵中第K小的元素 2.题解 1.优先队列 2.使用二分查找 class Solution { public:int kthSmallest(vector< ...

  8. python n个list如何组成矩阵_有序矩阵中第K小的元素amp;x的平方根(二分法篇)

    69. x的平方根 题目描述: 实现 int sqrt(int x) 函数. 计算并返回 x 的平方根,其中 x 是非负整数. 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去. 示例1: ...

  9. 分治法——查找问题 —— 寻找一个序列中第k小的元素和查找最大和次大元素

    查找问题: 问题一:寻找一个序列中第k小的元素 对于给定的含有n个元素的无序序列,求这个序列中第k(1<=k<=n)小的元素 分析思路: 假设无序序列存放在a[0 - n-1]中,若将a递 ...

  10. 九度OJ 题目1534:数组中第K小的数字(二分解)

    题目链接:点击打开链接 题目描述: 给定两个整型数组A和B.我们将A和B中的元素两两相加可以得到数组C. 譬如A为[1,2],B为[3,4].那么由A和B中的元素两两相加得到的数组C为[4,5,5,6 ...

最新文章

  1. 计算概论c和文科计算机,计算概论与计算机程序设计基础/C语言【理工学社】
  2. POJ 1321 棋盘问题【DFS】
  3. 【题解】 hdu2955 Robberies
  4. python no module name_python导包显示No module named XXX问题
  5. matlab cameraman,cameraman.tif 原图
  6. 叉叉助手停运有什么替代的_亚马逊全球开店助手将停止服务!卖家:终究是错付了...
  7. MR/hive/shark/sparkSQL
  8. UIScrollViewDelegate-代理API详解
  9. 2019.01.27【NOIP普及组】模拟赛C组总结
  10. Snowflake如日中天是否代表Hadoop已死?大数据体系到底是什么?
  11. iOS 核心动画 Core Animation浅谈
  12. php centos mysql_Linux+Apache+PHP+MySQL服务器环境(CentOS篇)
  13. python函数拟合求导_python – 使用scipy curve_fit通过两个数据点拟合指数函数
  14. Python_016 XML解析
  15. Android播放在线音乐文件
  16. Java一步到位!彻底了解JDK、JRE、JVM分别是什么及它们之间的联系
  17. Echarts经典颜色搭配
  18. java中print的含义_【转】Java中print、printf、println的区别详解
  19. VBA学习笔记4:将同一文件下的多个工作簿的数据汇总为一个工作表
  20. 企业上云成趋势 看超融合如何开箱即用、一步上云

热门文章

  1. DWR学习笔记--转载
  2. 091030 T 焦点在外,框架API设计
  3. java中Map,List与Set的区别
  4. wordpress知更鸟begin主题添加菜单字体图标
  5. linux下tomcat缓存磁盘文件,Linux环境下清理Tomcat缓存
  6. cpu性能参数如何看?
  7. OpenCV-图像处理(07、绘制形状与文字)
  8. 【UVA12230】Crossing Rivers(概率/期望)
  9. PAT 乙级A1025 适合当算法入门练习题做
  10. python 列表的行 列长度_Python连载|Pandas手册(上)