原题
题目描述
您现在在一家大工厂里。可以将工厂看作为具有n个顶点和m个边的图。每个边都有其长度。您有k个任务要做。第i个任务为达顶点 a i a_i ai,拾取一个块,然后将其发送到顶点 b i b_i bi。您应该按照从1号到k号的顺序完成任务。最初,您站在顶点1。
你手里拿着枪。当您处于某个顶点u时,您可以向地面射击,然后将在顶点u建立一个传送门。当工厂中有两个传送门时,假设它们分别位于u和v处,则可以在u和v之间进行瞬间转移(就像连接长度为0的u和v的边一样)。
您的手边还有一个遥控器。它使您可以随时随地关闭传送门(一次关闭一个传送门,而不是一次关闭所有传送门)。并且,最多可以有两个现有传送门。因此,如果要在存在两个传送门时创建另一个传送门,则必须先使用控制器将其关闭,然后再创建。
您需要找到必须步行的最小距离才能完成所有k个任务。
样例1
输入
5 4 2
1 2 1
2 3 1
3 4 1
4 5 1
1 5
2 4
输出
5
样例2
输入
6 10 3
1 1 6
5 6 9
3 5 8
1 4 1
2 4 7
6 6 10
1 4 2
6 5 10
3 5 2
3 1 9
1 5
2 5
4 3
输出
28
样例3
输入
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
输出
16
思路
首先,我们看见这道题的数据范围较小,所以考虑搜索。
dp[i][j][k]表示完成了i个任务在节点j,有一个传送门在k点。
但是由于这个算法的时间复杂度是O(n4),所以会TLE,需要优化。
我们可以把节点压在任务中,具体细节看代码。
这个算法的时间复杂度为O(n3),可以过。
代码
#include
using namespace std;
const int maxn=305,maxx=4e5+5;
long long n,m,nm,ans,u[maxx],v[maxx],w[maxx],a[maxn*2],dp[maxn*2][maxn],dis[maxn][maxn];
int main()
{
scanf("%lld%lld%lld",&n,&m,&nm);
memset(dis,0x16,sizeof(dis));
memset(dp,0x16,sizeof(dp));
dp[0][1]=0;ans=1e18;a[0]=1;
for(int i=1;i<=n;i++)
dis[i][i]=0;
for(int i=1;i<=m;i++)
scanf("%lld%lld%lld",&u[i],&v[i],&w[i]),dis[u[i]][v[i]]=dis[v[i]][u[i]]=min(dis[v[i]][u[i]],w[i]);
for(int i=1;i<=nm*2;i++)
scanf("%lld",&a[i]);
for(int k=1;k<=n;k++)//Floyd求最短路
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=1;i<=nm*2;i++)//DP
for(int j=1;j<=n;j++)
{
dp[i][j]=min(dp[i][j],dp[i-1][j]+min(dp[j][a[i]],dis[a[i]][a[i-1]]));
for(int k=1;k<=n;k++)dp[i][k]=min(dp[i][k],dp[i-1][j]+min(dis[j][k]+dis[k][a[i]],dis[a[i-1]][k]+dis[j][a[i]]));
}
for(int i=1;i<=n;i++)ans=min(ans,dp[nm*2][i]);
printf("%lld\n",ans);
return 0;
}