动态规划--训练合集

Portal

链接:https://ac.nowcoder.com/acm/contest/5670/A
来源:牛客网

题目描述

You are now in a big factory. The factory could be recognized as a graph with n vertices and m edges. Every edge has its length. You have k missions to do. The i-th mission is going to vertex a i a_i ai picking a block and then sending it to vertex b i b_i bi. You should complete the missions in the order from 1-st to k-th. Initially you are standing at vectex 1.

You have a gun in your hand. When you are at some vertex u, you could shoot the gun at the ground, and then a portal will be built at vertex u. When there are two portals in the factory, assuming they are at u and v, you could transfer between u and v with no cost(just like an edge connecting u and v with length 0).

You also have a remote controller in your hand. It allows you to close a portal whenever you want and wherever you are(closing one portal at a time, not all portals at the same time). What’s more, there could be at most two existing portals. So if you want to create another portal when there exists two, you must use your controller to close one before you create.

You need to find the minimum distance you have to walk in order to complete all k missions.
输入描述:
First line contains three positive integers n, m, k separated by spaces.

Next m lines, each contains three integers u i , v i , w i u_i, v_i, w_i ui,vi,wi
, indicating a bidirectional edge connecting vertex u i u_i ui
and v i v_i vi with length w i w_i wi .

Next k lines, each contains two intergers a i , b i a_i,b_i ai,bi , indicating the begin and the end of the i-th mission.
1 ≤ n ≤ 300 1 \leq n \leq 300 1n300
1 ≤ m ≤ 140000 1 \leq m \le1 40000 1m140000
1 ≤ k ≤ 300 1 \leq k \leq 300 1k300
1 ≤ u , v ≤ n 1\leq u,v \leq n 1u,vn
1 ≤ w ≤ 1 e 9 1\leq w \leq 1e9 1w1e9
The graph is guaranteed to be connected.
输出描述:
Output one integer indicating the minimum distance.
示例1
输入
5 4 2
1 2 1
2 3 1
3 4 1
4 5 1
1 5
2 4
输出

5
说明
Solution for sample 1: walk from 1 to 5, create portals at 2 and 4 when passing by. And then walk from 5 to 4, then you could use portals to complete the second mission.

题解

我也懒得写了,没仔细看题wa了一晚上
整体思路还是要动态规划,简化状态,要理解到,并不需要记录两个门的位置,因为可以随时在脚下新建一扇门。可以优化掉朴素转移方程中的一维,再想办法优化掉一维

动态规划--训练合集_第1张图片
AC代码:

#include
using namespace std;
typedef long long ll;
#define INF 9999999999999999
int n, m, k;
int c[1000];
const int maxn = 1000;
ll dis[maxn][maxn];
ll dp[maxn][maxn];

int main()
{
	memset(dis, 0x3f, sizeof(dis));
	cin >> n >> m >> k;
	for (int i = 1; i <= n; ++i)dis[i][i] = 0;
	for (ll i = 0, u, v, w; i < m; ++i)
	{
		cin >> u >> v >> w;
		dis[u][v] = min(dis[u][v], w), dis[v][u] = min(dis[v][u], w);
	}
	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]);
	k = k * 2;
	for (int i = 1; i <= k; ++i)cin >> c[i];
	memset(dp, 0x3f, sizeof(dp));
	for (int x = 1; x <= n; ++x)dp[1][x] = dis[1][x] + dis[x][c[1]];
	for (int i = 1; i < k; ++i)
	{
		for (int j = 1; j <= n; ++j)
		{
			if (dp[i][j] > INF)continue;
			//1. do not use door j, leave a door at x
			for (int x = 1; x <= n; ++x)
				dp[i + 1][x] = min(dp[i + 1][x], dp[i][j] + dis[c[i]][x] + dis[x][c[i+1]]);
			//2. do not use door j,leave a door at j/c[i]/c[i+1]
			dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + dis[c[i]][c[i + 1]]);
			dp[i + 1][c[i]] = min(dp[i + 1][c[i]], dp[i][j] + dis[c[i]][c[i + 1]]);
			dp[i + 1][c[i+1]] = min(dp[i + 1][c[i+1]], dp[i][j] + dis[c[i]][c[i + 1]]);
			//3. use door j(the best is c[i] -> j), then leave a door at x(x may == j)
			for (int x = 1; x <= n; ++x)
				dp[i + 1][x] = min(dp[i + 1][x], dp[i][j] + dis[j][x] + dis[x][c[i + 1]]);
			//4. use door j(the best is c[i] -> j), then leave a door at c[i]
			dp[i + 1][c[i]] = min(dp[i + 1][c[i]], dp[i][j] + dis[j][c[i + 1]]);
		}
	}
	ll ans = INF;
	for (int i = 1; i <= n; ++i)ans = min(ans, dp[k][i]);
	cout << ans << endl;

	return 0;
}

Debuging

题目描述:

现在有一个n行代码的程序,在运行的过程中崩溃了,你需要通过添加printf语句确认是哪一行代码有问题。无论添加多少printf语句,程序编译运行一次的时间都是r,添加一行printf语句耗时p,问最优debug方案的最差需要多少时间。

解析

d p [ i ] dp[i] dp[i]表示i行代码的最优方案最差时间
d p [ i ] = m i n { k ∗ p + d p [ c e i l ( n / ( k + 1 ) ) ] + r } 1 ≤ k ≤ n − 1 dp[i] = min\{ k * p + dp[ceil(n/(k + 1))] + r\}1\leq k \leq n-1 dp[i]=min{kp+dp[ceil(n/(k+1))]+r}1kn1
显然可以注意到存在多个k使得ceil(n/(k + 1))的值一样,此种情况下计算需要的最小的k即可,可以减少很多无用状态。
考虑到n小于1e5,需要优化计算复杂度

  • 传统递归计算:太慢
  • 传统dp递推:太多无用状态,eg:dp[99999], O ( n 2 ) O(n^2) O(n2)太慢
  • 记忆化搜索:okay!

AC代码:

#include
using namespace std;

typedef long long ll;

ll n, r, p;
ll dp[1000000 + 10];
ll dfs(ll n)
{
	if (dp[n] != -1)return dp[n];
	ll ret = LLONG_MAX;
	ll y = 1;
	while (y < n)
	{
		ll t = ceil(1.0 *n / (y + 1));
		ll tmp = y * p + r + dfs(t);
		ret = min(tmp, ret);
		y = max(y + 1, (ll)ceil(1.0 * n / (t - 1)) - 1);
	}
	return dp[n] = ret;
}


int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	
	memset(dp, -1, sizeof(dp));
	cin >> n >> r >> p;
	dp[0] = 0, dp[1] = 0, dp[2] = r + p;
	ll ans = dfs(n);
	cout << ans << endl;
	
	//system("pause");
	return 0;
}

你可能感兴趣的:(题解,动态规划,算法)