【日常训练】【BZOJ】20210304_T1_旅行travel_图论/最短路/Dijkstra_Bzoj4681

题面

HZY 喜欢旅游,这次她打算去一个据说有很多漂亮瀑布的山谷玩。

HZY 事先得到了一张地图,上面标注了 N 个小动物的聚居地,也就是一个个的小村落。其中第 1 个村庄是 HZY 现在住的地方,第 N 个村庄是 WJJ 打算去的地方。这些村庄之间有 M 条双向道路连接着,第 i 条双向道路恰好直接连接两个小村庄 A, B,长度为 C(1< = A, B < = N, A!=B, 1< = C< = 1000)。

道路有的是隧道,有的是栈桥,地图上那些看起来在村庄之外交叉的路实际上并不相交——也就是说,如果把这些小村落和双向道路构成的道路网看作图论意义上的图,我们不保证它是平面图,也不保证它没有重边。不过,有一点还是可以保证的: HZY细心地验证过, 从它居住的村落一定能走到她想去的那个山谷。

在 HZY 所在的神奇世界中,每只小动物都可以借助仙人掌来施放魔法,其中之一是, 交换世界中任意两条双向道路的长度,同时保持其他道路的长度不变。

按 HZY 目前的魔法水平,她最多能使用 K 次这种道路长度交魔法。可惜的是,由于仙人掌刺比较多, HZY 并不打算带着它旅行,于是她会在家里完成想要的道路交换后再出门。假设 HZY 的旅行途中不会有其他小动物进行道路交换来破坏她设计好的路线。为了尽快达到目的地, HZY 希望她需要走的总距离越短越好。也就是说, 使用最多 K 次魔法后,从村落 1 到村落 N 的最短距离是多少?

对于前 10%的数据,N≤10;

对于前 30%的数据,N≤20;

对于前 40%的数据,K≤4;

对于 100%的数据,2≤N≤50,1≤M≤150,K≤20。

黑暗爆炸OJ 4681

题解

现场得分:30/100

因为我dp的第二维应该是到(k)的,但是因为没有想清楚,在初始化dp数组的时候第二维到(n)了。

  • 我们考虑如果一条最短的路径,一定是把某一条路径其中最大的(k)条边变成了最短的(k)条边。假设我们这条路径上[(经过变化的边)中变化后最大的那条边]变成了所有边中排名第(j)的边,则所有排名小于等于(j)的边一定是全部使用了的。(如果有边边权相等,任取即可,不影响)
  • 那么我们可以枚举一个(j),表示强制要求边权前(j)小的边全部使用,我们的答案是多少。我们可以通过(f_{i,d,h})表示到第(i)个点,当前变化了(d)次,前(j)个中已经用了(h)个的情况下,走过的边权不在前(j)小的边权和最小值。
  • 直接Dij转移,复杂度(O(n^2km))

代码

#include<bits/stdc++.h>
#define LL long long
#define U unsigned
#define MAXN 51
#define MAXM 160
#define MAXK 21
using namespace std;
template<typename T> void Read(T &cn)
{
	char c; int sig = 1;
	while(!isdigit(c = getchar())) if(c == '-') sig = 0;
	if(sig) {cn = c-48; while(isdigit(c = getchar())) cn = cn*10-48+c; }
	else    {cn = 48-c; while(isdigit(c = getchar())) cn = cn*10+48-c; }
}
template<typename T> void Write(T cn)
{
	T cm = 0; int wei = 0, cx = cn%10; cn = cn/10;
	if(cn < 0 || cx < 0) {putchar('-'); cn = -cn; cx = -cx; }
	while(cn) wei++, cm = cm*10+cn%10, cn = cn/10;
	while(wei--) putchar(cm%10+48), cm = cm/10;
	putchar(cx+48);
}
template<typename T> void WriteL(T cn) {Write(cn); puts(""); }
template<typename T> void WriteS(T cn) {Write(cn); putchar(' '); }
template<typename T> void Max(T &cn, T cm) {cn = cn < cm ? cm : cn; }
template<typename T> void Min(T &cn, T cm) {cn = cn < cm ? cn : cm; }
struct qwe{
	int a,b,ne,w,pai;
	void mk(int cn, int cm, int cx, int cw) {a = cn; b = cm; ne = cx; w = cw; }
};
struct qwer{
	int i, d, h, zhi, pos;
	void mk(int ci, int cd, int ch, int cz, int cp) {i = ci; d = cd; h = ch; zhi = cz; pos = cp; }
	inline friend bool operator <(qwer cn, qwer cm) {return cn.zhi == cm.zhi ? (cn.pos < cm.pos) : cn.zhi > cm.zhi; }
};
qwe a[MAXM*2+1];
int alen;
int head[MAXN+1];
int n,m,k,curj,curk;
int f[MAXN+1][MAXK+1][MAXN+1];
qwer dui[MAXN*MAXK*MAXM*2+1];
int dlen, plen;
void lian(int cn, int cm, int cx) {a[++alen].mk(cn,cm,head[cn],cx); head[cn] = alen; }
void zeng(int ci, int cd, int ch, int cz) 
{
	if(ch > curj || cd > ch) return;
	if(cd > curk) return;
	if(f[ci][cd][ch] <= cz) return;
	f[ci][cd][ch] = cz; 
	++plen; dui[++dlen].mk(ci, cd, ch, cz, plen); push_heap(dui+1,dui+dlen+1); 
}
void suan()
{
//	printf("in suan : curj = %d
",curj);
	plen = dlen = 0;
	for(int i = 1;i<=n;i++) for(int d = 0;d<=curk;d++) for(int h = 0;h<=curj;h++) f[i][d][h] = 2000*n;
	zeng(1, 0, 0, 0);
	while(dlen)
	{
		while(dlen && (f[dui[1].i][dui[1].d][dui[1].h] != dui[1].zhi)) pop_heap(dui+1,dui+(dlen--)+1);
		if(!dlen) return;
		int dang = dui[1].i, bx = dui[1].d, by = dui[1].h, bz = dui[1].zhi;
//		printf("  dang = %d bx = %d by = %d bz = %d
",dang,bx,by,bz);
		pop_heap(dui+1,dui+(dlen--)+1);
		for(int i = head[dang];i;i = a[i].ne)
		{
			int y = a[i].b;
			if(a[i].pai <= curj) {zeng(y, bx, by+1, bz); continue; }
			zeng(y, bx+1, by+1, bz);
			zeng(y, bx, by, bz+a[i].w);
		}
	}
}
signed main()
{
//	freopen("travel.in","r",stdin);
//	freopen("travel.out","w",stdout);
	Read(n); Read(m); Read(k);
	alen = 0; memset(head,0,sizeof(head));
	for(int i = 1;i<=m;i++) {int bx,by,bz; Read(bx); Read(by); Read(bz); lian(bx,by,bz); lian(by,bx,bz); }
	for(int i = 1;i<=m;i++)
	{
		a[i*2].pai = 1;
		for(int j = 1;j<i;j++) if(a[i*2].w >= a[j*2].w) a[i*2].pai++;
		for(int j = i+1;j<=m;j++) if(a[i*2].w > a[j*2].w) a[i*2].pai++;
		a[i*2-1].pai = a[i*2].pai;
//		printf("a[%d].pai = %d
",i*2,a[i*2].pai);
	}
	int ans = 2000*n;
	for(int i = 0;i<=n;i++) 
	{
		curj = i; curk = min(i,k); suan();
		int lin = 2000*n;  
		for(int d = 0;d<=curk;d++) for(int h = 0;h<=i;h++) Min(lin, f[n][d][h]);
		for(int j = 1;j<=m;j++) if(a[j*2].pai <= i) lin = lin + a[j*2].w;
//		printf("i = %d lin = %d
",i,lin);
		Min(ans, lin);
	}
	WriteL(ans);
	return 0;
}
原文地址:https://www.cnblogs.com/czyarl/p/14482409.html