题目
题目大意
您现在在一家大工厂里。可以将工厂看作为具有n个顶点和m个边的图。每个边都有其长度。您有k个任务要做。第i个任务为达顶点ai,拾取一个块,然后将其发送到顶点bi。您应该按照从1号到k号的顺序完成任务。最初,您站在顶点1。
你手里拿着枪。当您处于某个顶点u时,您可以向地面射击,然后将在顶点u建立一个传送门。当工厂中有两个传送门时,假设它们分别位于u和v处,则可以在u和v之间进行瞬间转移(就像连接长度为0的u和v的边一样)。
您的手边还有一个遥控器。它使您可以随时随地关闭传送门(一次关闭一个传送门,而不是一次关闭所有传送门)。并且,最多可以有两个现有传送门。因此,如果要在存在两个传送门时创建另一个传送门,则必须先使用控制器将其关闭,然后再创建。
您需要找到必须步行的最小距离才能完成所有k个任务。
分析
此题先忽略掉传送门的存在跑一遍Floyd,然后采用dp进行对于传送门的考虑,其实仔细考虑一下情况不是很难。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll INF=1e18,maxn=305,maxm=4e5+5;
int n,m,kk,c[maxn<<1];
ll dis[maxn][maxn],f[maxn<<1][maxn];
int main()
{
scanf("%d%d%d",&n,&m,&kk);c[0]=1;
for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) if(i==j) dis[i][j]=0;else dis[i][j]=INF;
for(int i=1;i<=m;i++)
{
int x,y;ll z;
scanf("%d%d%lld",&x,&y,&z);dis[x][y]=dis[y][x]=min(dis[x][y],z);
}
for(int i=1;i<=kk;i++)
{
int x,y;scanf("%d%d",&x,&y);
c[(i<<1)-1]=x,c[i<<1]=y;
}
for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
for(int i=0;i<=2*kk;i++) for(int j=1;j<=n;j++) f[i][j]=INF;f[0][1]=0;
for(int i=1;i<=2*kk;i++)
{
int s=c[i-1],t=c[i];
for(int j=1;j<=n;j++)
{
f[i][j]=min(f[i][j],f[i-1][j]+dis[s][t]);
for(int k=1;k<=n;k++)
f[i][k]=min(f[i][k],f[i-1][j]+dis[s][k]+dis[j][t]),
f[i][k]=min(f[i][k],f[i-1][j]+dis[s][k]+dis[k][t]),
f[i][k]=min(f[i][k],f[i-1][j]+dis[j][k]+dis[k][t]),
f[i][k]=min(f[i][k],f[i-1][j]+dis[j][k]+dis[j][t]);
}
}
ll ans=INF;
for(int i=1;i<=n;i++) ans=min(ans,f[2*kk][i]);
printf("%lld",ans);
}
—原创文章,仅供参考