BZOJ 1922

本文探讨了一种特殊的图论问题,即在一个拓扑结构中寻找最短路径的方法。问题的核心在于,要到达某个节点必须先经过其保护节点。文章通过Dijkstra算法实现了解决方案,并考虑了节点间的关系及保护节点的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天打了一道题,
据说是分层图,然后有人告诉我是拓扑,
有一些事情,我们不得不面对,
但我不会再留在原地
一定得做些什么啊
题意就是要走一个点,必须要先走过保护她的点。
然后求最短路。
//这里注意的是,要先便利保护最短路上的点,的点
//或者另一种是思路,是一边遍历一遍放点,但需要一个严格证明
//这里有一个状态的继承很像分层图
//可能要用dij来跑了,因为spfa好像不能保证一次更新出最小值(这是由于dij的贪心思想,用最小的来更新) (这里的最短不是指d[y] = d[x] + z中的y一次更新后直接最短,而是指x取出时直接是最短的了(第一次取出的x就是最短的,而用spfa第一次与第二次取出的x可能会不同))
//由于下面写的“d[ee[i].ver] = max(d[x],d[ee[i].ver]);”,会导致如果spfa第一次更新得出的保护这个点A的点B的最短路大于直接到点A的最短路,那么到点B的最短路就会被更新成这个值,而如果下一次更新到这个点A的最短路小于刚刚跟新的到A的值,尽管这次得出的值才是最短的,由于我们取了max,不会进行最短路的更新,致错;
//那么,对于每一个点,都是第一次便利时就是最短路了;
//按普通的算法新添上的点可以对已有的点的最短路进行更新,但是如果让每一个新添上的点的最短路进行继承就可以了;(这时,这个点的最短路应当是大于原有的最短路的点的,
//按照原有的思路,应该是继承并且进行一边释放一边跑最短路
//但这样你要如何保证会将每一个点的最大值都得出
//开一个sum记录点的保护树
//如果是这个点可以被遍历所消耗的时间,小于便利这个点的时间,就需要让便利这个点的时间作为d
//但是还是有了一些问题,比如,当这个点第一次便利时sum不等于0,而后又将sum变为0,但是不会在便利这个点了

#include<bits/stdc++.h> 
using namespace std;
const int maxn = 140090;
const int maxxn = 3090;
#define inf 0x3f3f3f
priority_queue<pair<int ,int > > q;//先写个正常的dij
 
 
int n,m,k,tot = 0,tt = 0;
int ans = 21474340;
int sum[maxxn];
int head[maxxn];
bool v[maxxn];
int id[maxxn];//仿照邻接表建造 
int d[maxxn];
struct node {
    int fr,ver,value,Next;
};
node edge[maxn];
struct nodd {
	int fr,ver,Next;
};
nodd ee[maxn];
 
inline void ddd (int x,int y) {
	ee[++tt].ver = y; ee[tt].fr = x; ee[tt].Next = id[x]; id[x] = tt;
}
inline void add (int x,int y,int z)  {
    edge[++tot].fr = x; edge[tot].ver = y; edge[tot].value = z; edge[tot].Next = head[x]; head[x] = tot;
}
inline int Read () {
    int xx = 0;
    int ff = 1;
    char ch = getchar();
    while (ch < '0'||ch > '9') {
        if (ch == '-') ff = -1;
        ch = getchar();
    }
    while (ch >= '0'&&ch <= '9') {
        xx = (xx << 1) + (xx << 3) + ch - '0';
        ch = getchar();
    }
    return xx * ff;
}
void dij() {
	d[1] = 0;
	q.push(make_pair(0,1));
	while (!q.empty()) {
		int x = q.top().second;
		q.pop();
		if (v[x] == 1) continue;
		v[x] = 1;
		for (int j = head[x];j;j = edge[j].Next) {
			int z = edge[j].value;
			int y = edge[j].ver;		
			if (d[y] > d[x] + z) {
				d[y] = d[x] + z;
				if(sum[y] == 0)	q.push(make_pair(-d[y],y));
			}
		}
		for (int i = id[x];i;i= ee[i].Next) {
			sum[ee[i].ver]--;//遍历所有被这个点保护的点,并且将他们的sum-1
			//如果放到上一个循环中,就会导致只有遍历到x的一条边才会减一,如果x没有边了(一个终点),就没有办法进行继承了 
			d[ee[i].ver] = max(d[x],d[ee[i].ver]);
			if (sum[ee[i].ver] == 0) {
				q.push(make_pair(-d[ee[i].ver],ee[i].ver));
			} 
		}
	}
}
 
 
int main () {
    n = Read(); m = Read(); 
    memset(head,0,sizeof(head));
    for (int i = 1;i <= m;i++) {
        int a = Read(); int b = Read(); int c = Read();
        add(a,b,c); 
    }
    memset(id,0,sizeof(id));
    memset(sum,0,sizeof(sum));
    for (int j = 1;j <= n;j++) {
    	int nn = Read();
    	for (int i = 1;i <= nn;i++) {
    		int a = Read(); 
    		ddd (a,j);
    		sum[j]++;
		}
	}
    memset(v,0,sizeof(v));
    memset(d,inf,sizeof(d));
	dij();
    printf("%d\n",d[n]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值