拓扑排序+迪杰斯特拉:https://www.acwing.com/problem/content/344/
首先我们可以观察到:
1.整个图的形状是一个个通过道路相连的团,而团与团之间通过航线相连。
2.注意到团的非负性,可以用迪杰斯特拉,考虑到航线的单向性,我们可以考虑拓扑排序。
具体的,我们把他们融合在一起:
1.先输入道路扫出连通块。
2.再输入航线确定入度
3.按照拓扑序处理每一个连通块,当其中一个连通块入度为0时取出最小的放入队列中。
AC代码:
- #include
- using namespace std;
- #define x first
- #define y second
-
- int t,r,p,s;
- int id[250100];
- int inf=0x3f3f3f3f;
- struct node{
- int dian,w;
- };
- vector<int> block[250100];
- vector
edge[250100]; - int cnt=0;
- int deg[250100];
- int dis[250100];
- int st[250100];
- void dfs(int x,int c){
- id[x]=c;
- block[c].push_back(x);
- for(int i=0;i
size();i++){ - int ck=edge[x][i].dian;
- if(id[ck]) continue;
- dfs(ck,c);
- }
- }
- void dij(){
- priority_queue
int,int>,vectorint,int> >,greaterint,int> > > q;//(距离,编号) - memset(dis,0x3f,sizeof(dis));
- dis[s]=0;
- for(int i=1;i<=cnt;i++){
- if(deg[i]) continue;
- for(int j=0;j
size();j++){ - int x=block[i][j];
- q.push({dis[x],x});
- }
- }
- while(!q.empty()){
- auto ck=q.top();
- q.pop();
- if(st[ck.y]) continue;
- st[ck.y]=1;
-
- for(int i=0;i
size();i++){ - auto fk=edge[ck.y][i];
- if(dis[fk.dian]>dis[ck.y]+fk.w){
- dis[fk.dian]=dis[ck.y]+fk.w;
- if(id[fk.dian]==id[ck.y]) q.push({dis[fk.dian],fk.dian});
- }
- }
- for(int i=0;i
size();i++){ - auto fk=edge[ck.y][i];
- if(id[fk.dian]!=id[ck.y]){
- deg[id[fk.dian]]--;
- if(deg[id[fk.dian]]==0){
- for(int j=0;j
size();j++){ - int x=block[id[fk.dian]][j];
- q.push({dis[x],x});
- }
- }
- }
- }
- }
- }
- int main(){
- cin>>t>>r>>p>>s;
- for(int i=1;i<=r;i++){
- int a,b,c;
- cin>>a>>b>>c;
- edge[a].push_back({b,c});
- edge[b].push_back({a,c});
- }
- //找连通块
- for(int i=1;i<=t;i++){
- if(id[i]==0) dfs(i,++cnt);
- }
- //加入航线
- for(int i=1;i<=p;i++){
- int a,b,c;
- cin>>a>>b>>c;
- edge[a].push_back({b,c});
- deg[id[b]]++;
- }
- //dij
- dij();
- for(int i=1;i<=t;i++){
- if(dis[i]>=inf/2) cout<<"NO PATH"<
- else cout<
- }
- }
二分+单源最短路:
首先二分出来一个x,我们想知道一条路上至少有几个是大于X的。我们每一次可以把边权大于X的看成1,比他小的看成0,于是问题就转换成了求单源点的最短路。
AC代码:
- #include
- using namespace std;
- const int N = 1010, M = 20010;
- int n, m, k;
- int h[N], e[M], w[M], ne[M], idx;
- int dist[N];
- deque<int> q;
- bool st[N];
- void add(int a, int b, int c)
- {
- e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
- }
- bool check(int bound)
- {
- memset(dist, 0x3f, sizeof dist);
- memset(st, 0, sizeof st);
-
- q.push_back(1);
- dist[1] = 0;
-
- while (q.size())
- {
- int t = q.front();
- q.pop_front();
-
- if (st[t]) continue;
- st[t] = true;
-
- for (int i = h[t]; ~i; i = ne[i])
- {
- int j = e[i], x = w[i] > bound;
- if (dist[j] > dist[t] + x)
- {
- dist[j] = dist[t] + x;
- if (!x) q.push_front(j);
- else q.push_back(j);
- }
- }
- }
- return dist[n] <= k;
- }
-
- int main()
- {
- cin >> n >> m >> k;
- memset(h, -1, sizeof h);
- while (m -- )
- {
- int a, b, c;
- cin >> a >> b >> c;
- add(a, b, c), add(b, a, c);
- }
- int l = 0, r = 1e6 + 1;
- while (l < r)
- {
- int mid = l + r >> 1;
- if (check(mid)) r = mid;
- else l = mid + 1;
- }
- if (r == 1e6 + 1) cout << -1 << endl;
- else cout << r << endl;
- }
DP+最短路:https://www.acwing.com/problem/content/343/
可以观察到:
从源点1到终点n一定存在一个分界点i,在上取到最小值,在上取到最大值。
于是我们记表示上的最大值,表示上的最小值,然后我们枚举每一个即可。
现在我们主要求这个数组,假如图是严格的拓扑序,那么DP转移就十分明显,但是现在的图可能存在回路,直接DP会陷入死循环,于是我们考虑最短路的思想。
我们把距离的概念抽象成上面的两个数组的值,松弛操作也就是
注意虽然求单源最短路可以迪杰斯特拉,但是他的前提是每一次第一出队的一定是最短的,而显然这个条件在这里不成立(因为后面的可能比当前出对的更小)。
那么SPFA?它的本质是动态规划,每一次在前一次的基础上松弛,保证了走到某一点在i步范围内是正确的,进而不断转移到n步,(这在没有负环的情况下显然是包含所有情况)而在本题中不断走一个环没有意义,也就是不存在负环。因此SPFA是适用的。
AC代码:
- #include
- using namespace std;
- int n,m;
- int a[100010];
- vector<int> edge[500010];
- vector<int> edeg[500010];
- int maxx[100010],minn[100010];
- int st[100010];
- void spfa(int type)
- {
- queue<int> q;
- memset(st, 0, sizeof st);
- if(type==0){
- memset(minn, 0x3f, sizeof(minn));
- minn[1] = a[1];
- q.push(1);
- st[1] = true;
- }
- else{
- memset(maxx, 0xc0, sizeof(maxx));
- maxx[n]=a[n];
- q.push(n);
- st[n]=1;
- }
- while (!q.empty())
- {
- int t = q.front();
- st[t] = false;
- q.pop();
- if(type==0){
- for (int i =0; i
size(); i ++) - {
- int j = edge[t][i];
- if (minn[j] > min(minn[t],a[j]))
- {
- minn[j] = min(minn[t],a[j]);
- if (!st[j]) // 如果队列中已存在j,则不需要将j重复插入
- {
- q.push(j);
- st[j] = true;
- }
- }
- }}
- else{
- for (int i =0; i
size(); i ++){ - int j=edeg[t][i];
- if(maxx[j]<max(maxx[t],a[j])){
- maxx[j]=max(maxx[t],a[j]);
- if (!st[j]) // 如果队列中已存在j,则不需要将j重复插入
- {
- q.push(j);
- st[j] = true;
- }
- }
- }
- }
- }
- }
-
- int main(){
- cin>>n>>m;
- for(int i=1;i<=n;i++) scanf("%d",&a[i]);
- for(int i=1;i<=m;i++){
- int x,y,z;
- cin>>x>>y>>z;
- edge[x].push_back(y);
- edeg[y].push_back(x);
- if(z==2){
- edge[y].push_back(x);
- edeg[x].push_back(y);
- }
- }
- spfa(0);
- spfa(1);
- int res=0;
- for(int i=1;i<=n;i++) res=max(res,maxx[i]-minn[i]);
- cout<
- }
-
相关阅读:
【网络安全】什么样的人适合学?该怎么学?
【数据结构】测试3 栈和队列
【JavaEE】锁策略
Leetcode—3148. 矩阵中的最大得分【中等】
算法练习16——O(1) 时间插入、删除和获取随机元素
房产新闻查询易语言代码
spring5.0 源码解析(day05) initMessageSource();
K-verse 小型活动来袭!
SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.14 ES 索引操作
配运基础数据缓存瘦身实践
-
原文地址:https://blog.csdn.net/cocoack/article/details/140994014