牛客多校第五场 A Portal 图 dp

本文讨论了一道名为'Portal'的图论问题,任务是找到完成一系列点对访问的最短距离,其中可以放置两个传送门来减少行走距离。作者探讨了暴力动态规划方法以及优化后的解决方案,提出转移状态可以简化为dp[i][j],表示完成了i个任务且传送门位于点j。虽然尝试了不同的转移策略,但未能给出完整有效的转移方程。
摘要由CSDN通过智能技术生成

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值