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

题目

传送门

题目大意

您现在在一家大工厂里。可以将工厂看作为具有n个顶点和m个边的图。每个边都有其长度。您有k个任务要做。第i个任务为达顶点ai,拾取一个块,然后将其发送到顶点bi。您应该按照从1号到k号的顺序完成任务。最初,您站在顶点1。
你手里拿着枪。当您处于某个顶点u时,您可以向地面射击,然后将在顶点u建立一个传送门。当工厂中有两个传送门时,假设它们分别位于u和v处,则可以在u和v之间进行瞬间转移(就像连接长度为0的u和v的边一样)。
您的手边还有一个遥控器。它使您可以随时随地关闭传送门(一次关闭一个传送门,而不是一次关闭所有传送门)。并且,最多可以有两个现有传送门。因此,如果要在存在两个传送门时创建另一个传送门,则必须先使用控制器将其关闭,然后再创建。
您需要找到必须步行的最小距离才能完成所有k个任务。

分析

此题先忽略掉传送门的存在跑一遍Floyd,然后采用dp进行对于传送门的考虑,其实仔细考虑一下情况不是很难。

#include 
#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);
}

—原创文章,仅供参考

你可能感兴趣的:(图论)