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;
}