Portal
题目大意
刚刚给一个无向图,边权是距离,有k个任务,每个任务是要去两个点,必须先去ai点,后去bi点。任务必须从前往后做,也就是说不能做完1任务就做3任务。现在有两个传送门可以放置,放置传送门的时候必须走到那个点放置,撤掉传送门的时候可以不在那个点。
一个传送门可以直接传送到另一个传送门,距离为0。
刚开始在1号点,问完成任务需要走过的距离最少是多少?
点数:300,任务数:300,边数:4e4
题解
完成任务a1->b1->a2->b2->a3->b3…… 顺序肯定是这样走的。
所以不用把任务两个两个分开。
a1,b1,a2,b2,a3,b3设为ci。
最暴力的dp:dp[i][j][k][p] 代表 完成了i个任务,当前在点j,两个传送门一个在k一个在p。
这个显然不行的。
用不用记两个传送门?
用传送门的时候肯定是给脚下放一个传送门然后传到另一个传送门那里。
所以可以缩成dp[i][j][k]。
但是这样还是不行的。。可能是我不会转移,转移的时候写了4个for。所以我认为他不行。然后就没试了
不用记录当前在哪个点。直接从ci点转移就好
dp[i][j]表示当前走了i个点(在ci点处),传送门在j。
转移:
可以从dp[i][j]转移到dp[i+1][k]的情况:
走到点k处,把传送门安在那里,然后走到c[i+1].
走到k处,那传送门安到 k 处,传送到j处,再走到c[i+1]。
传送到j处,走到k处,放传送门在k处,走到c[i+1]。
这个转移覆盖了所有的情况
代码:
#include<algorithm>
#include<cstring>
#include <iostream>
#include <cstdio>
#include <queue>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<double,double> pdd;
typedef unsigned long long ull;
typedef set<int>::iterator sit;
#define st first
#define sd second
#define mkp make_pair
#define pb push_back
void tempwj(){freopen("hash.in","r",stdin);freopen("hash.out","w",stdout);}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;}
struct cmp{bool operator()(const pii & a, const pii & b){return a.second < b.second;}};
int lb(int x){return x & -x;}
//friend bool operator < (Node a,Node b) 重载
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int maxn = 305+10;
ll dp[605][maxn];
//dp[i][j]表示:目前完成了i个任务,一个传送门在j点
ll dis[maxn << 1][maxn];
int a[maxn << 1];
int main()
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for (int i= 1; i <= n; i ++ )
{
for (int j = 1; j <= n; j ++ )
dis[i][j] = INF;
dis[i][i] = 0;
}
for(int i = 1; i <= m; i ++ )
{
int x,y;
ll v;
scanf("%d%d%lld",&x,&y,&v);
dis[x][y] = dis[y][x] = min(dis[x][y], v);
}
for (int k = 1; k <= n; k ++ )
{
for(int i = 1; i <= n; i ++ )
{
for(int j = 1; j <= n; j ++ )
{
if(dis[i][j] > dis[i][k] + dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
for (int i = 1; i <= 2 * k; i ++ )
{
scanf("%d",&a[i]);
}
a[0] = 1;
for(int i = 0; i <= 2 * k; i ++ )
{
for(int j = 0; j <= n; j ++ )
dp[i][j] = INF;
}
dp[0][1] = 0;
for(int i = 0; i < 2 * k; i ++ )
{
for (int j = 1; j <= n; j ++ )
{
if(dp[i][j] == INF)
continue;
dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + dis[a[i]][a[i + 1]]);// 位置没有变化
for(int kk = 1; kk <= n; kk ++ )
{
dp[i + 1][kk] = min(dp[i][j] + dis[a[i]][kk] + dis[kk][a[i + 1]], dp[i + 1][kk]);
dp[i + 1][kk] = min(dp[i][j] + dis[a[i]][kk] + dis[j][a[i + 1]], dp[i + 1][kk]);//从k传送到j
dp[i + 1][kk] = min(dp[i][j] + dis[j][kk] + dis[kk][a[i + 1]], dp[i + 1][kk]);
}
}
}
ll ans = INF;
for (int i = 1; i <= n; i ++ )
{
ans = min(ans,dp[2 * k][i]);
}
printf("%lld\n", ans);
}