GJGHFD的传送门 题解 [Floyd+DP]

GJGHFD的传送门

Description:

​ 给定一张 \(n\) 个点 \(m\) 条边的连通图,图中每条边都有一个非负边权,点的编号从 1 开始. 现在你有 \(k\) 个任务,第 \(i\) 个任务需要你先到达点 \(a_i\), 然后到达点 \(b_i\). 你需要依次完成这 \(k\) 个任务. 初始时你在 1 号点.
​ 现在你手里有一把枪,当你在 \(u\) 号点朝地上开枪时,\(u\) 上就会建立一个传送门. 开枪消耗的时间可以忽略不计. 如果你在 \(u\), \(v\) 两点都建立了传送门,那么你就可以在这两点间不计消耗地任意传送(你可以将其视作图中加入了一条连接 \(u, v\) 且边权为 0 的边).
​ 遗憾的是,这张图中同时只能存在 2 个传送门. 因此当图中存在了 2 个传送门,且你需要再建立一个传送门时,你必须先选择一个已经建立的传送门并关闭它. 关闭传送门的消耗同样是忽略不计的,并且你可以关闭处在任
意位置的传送门,与你当前所在的位置无关.
​ 请求出完成这 \(k\) 个任务最少需要走过的距离.

Input:

​ 第一行三个整数 \(n, m, k\) ,分别代表图中点的个数、边的条数、任务的个数.
​ 接下来 \(m\) 行,每行三个整数 \(u, v, w\) ,表示图中有一条连接 \(u, v\) ,且边权为 \(w\) 的边.
​ 接下来 \(k\) 行,每行两个整数 \(a_i\), \(b_i\) ,表示第 \(i\) 个任务需要到达的两个点.

Output:

​ 输出一行一个整数表示答案.

Sample Input 1:

5 4 2
1 2 1
2 3 1
3 4 1
4 5 1
1 5
2 4

Sample Output 1:

5

Sample Input 2:

6 10 3
1 1 3
3 1 1
6 2 3
1 6 10
4 1 1
3 1 2
5 6 9
5 4 10
6 3 4
3 4 4
3 5
3 6
6 5

Sample Output 2:

16

Hint:

​ 对于\(30\%\)的数据,\(1 \leq n,k \leq 50\)

​ 另有\(30\%\)的数据,\(m=n-1\)

​ 对于\(100\%\)的数据,\(1 \leq n, k\leq 300,1 \leq m \leq 40000,0 \leq w \leq 10^9\)

​ 时间限制: \(1s\)

​ 空间限制: \(512M\)

题目分析:

​ 经过观察我们发现n很小,因此先跑一遍Floyd预处理出两两之间的最短路。

​ 然后我们再观察传送门的性质,可以发现,虽说有两个传送门,但实际上可以看作是一个传送门,然后你从任意一个点都能到达传送门所在的那个点(只需把另一个传送门放到你所在的点即可)。

​ 其次,所谓的\(K\)个任务是假的,其实就是\(K\times2\)个任务,每次从上一个点到达这一个点。

​ 于是我们考虑设\(f[i][j]\)表示完成前\(i\)个任务,当前有一个传送门在\(j\)的最优方案路径长。(\(1\leq i \leq K \times 2,1 \leq j \leq n\))

​ 考虑转移(x表示当前任务要到达的点,now表示上一个任务要到达的点(now初始化为1)):

​ 1.\(f[i][j]=f[i-1][j]+min(dis[j][x],dis[now][x]);\)

​ 若把传送门设在\(j\)这个位置,可以是之前传送门就在\(j\),于是我们可以传送到\(j\)然后走到\(x\),或者直接从上一次到达的点走到\(x\).

​ 2.\(f[i][j]=f[i-1][k]+min(dis[j][k],dis[now][k])+min(dis[k][x],dis[j][x])\);

​ 不难理解,若之前传送门在\(k\),那么我们为了把传送门设在\(j\)位置,可以先由\(now\)\(k\)位置走到\(j\),设下传送门,然后从\(j\)\(k\)位置走到\(x\).

​ 最后输出\(f[K][now]\)即可.

​ PS:我用的DP用了滚存优化空间( 实际没必要 ),

​ 代码如下(马蜂很丑,不喜勿喷)——

#include<bits/stdc++.h>
#define Tp template<typename T>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define maxn 305
#define maxm 80005
#define inf 214748364998244353
#define LL long long
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,K,tot,fir[maxn],nxt[maxm],son[maxm],w[maxm];LL ans=inf,f[2][maxn],dis[maxn][maxn];
inline void add(int x,int y,int z){son[++tot]=y,nxt[tot]=fir[x],fir[x]=tot,w[tot]=z;}
class FileInputOutput
{
	private:
		static const int S=1<<21;
		#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
		#define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch))
		char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[25];
	public:
		FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; }
		Tp inline void read(T& x)
		{
			x=0; char ch; while (!isdigit(ch=tc()));
			while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
		}
		Tp inline void write(T x,const char& ch)
		{
			if (x<0) pc('-'),x=-x; RI ptop=0; while (pt[++ptop]=x%10,x/=10);
			while (ptop) pc(pt[ptop--]+48); pc(ch);
		}
		inline void flush(void)
		{
			fwrite(Fout,1,Ftop-Fout,stdout);
		}
		#undef tc
		#undef pc
}F;
int main(){
	F.read(n),F.read(m),F.read(K);memset(dis,63,sizeof(dis));
	for(register int i=1,x,y,z;i<=m;i++) F.read(x),F.read(y),F.read(z),dis[x][y]=dis[y][x]=min(dis[x][y],(LL)z);
	for(register int i=1;i<=n;i++){for(register int j=1;j<=n;j++) for(register int k=1;k<=n;k++) dis[j][k]=min(dis[j][k],dis[j][i]+dis[i][k]);dis[i][i]=0;}
	K<<=1;int now=1;memset(f,63,sizeof(f));
	f[0][1]=0;for(register int k=1,x,op=(k&1);k<=K;now=x,op^=1,k++){
		int opp=op^1;F.read(x);for(register int i=1;i<=n;i++){
			f[op][i]=min(f[op][i],f[opp][i]+min(dis[i][x],dis[now][x]));
			for(register int j=1;j<=n;j++) f[op][j]=min(f[op][j],f[opp][i]+min(dis[i][j],dis[now][j])+min(dis[j][x],dis[i][x]));f[opp][i]=inf;
		}
	}
	F.write(f[K&1][now],'\n');return F.flush(),0;
}
posted @ 2021-01-05 19:23  OdtreePrince  阅读(211)  评论(0编辑  收藏  举报