2020牛客暑期多校训练营(第五场) Portal

原题
题目描述
您现在在一家大工厂里。可以将工厂看作为具有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;
} 

你可能感兴趣的:(2020牛客暑期多校训练营(第五场) Portal)