potyczki-algorytmiczne-2012 dwa 动态规划优化

题意

  • 给你两个排列 A , B A, B A,B,每次可以从一个排列中拿出其中的第一个数,当两个排列的第一个数不相同时可以同时拿出两个排列的第一个数,求最少多少次能把所有数拿完, n &lt; = 1 e 6 n&lt;=1e6 n<=1e6

不难想到设 d p [ i ] [ j ] dp[i][j] dp[i][j] A A A序列取到了 i i i, B B B序列取到了 j j j的最小步数

d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + 1 dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1 dp[i][j]=min(dp[i1][j],dp[i][j1])+1

如果 A [ i ] ! = B [ j ] , d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i − 1 ] [ j − 1 ] + 1 ) A[i] != B[j], dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1) A[i]!=B[j],dp[i][j]=min(dp[i][j],dp[i1][j1]+1)

这样子是 n 2 n^2 n2的,而且状态数就有 O ( n 2 ) O(n^2) O(n2)个,考虑优化状态数

发现 A [ i ] ! = A [ j ] A[i] != A[j] A[i]!=A[j]时 状态是可以合并的

那么我们来考虑一下压缩状态

f i f_i fi A A A取到 A i A_i Ai B B B取到与 A i A_i Ai相同的数(当前取了 A i A_i Ai B j B_j Bj中的一个)

这个时候取完所有数的最优答案

B B B取到了 B j B_j Bj g [ d ] g[d] g[d]表示与当前这位最接近的出现了距离差为 d d d i i i的位置

那么可以得到以下状态转移方程

f [ i ] = f [ g [ i − j − 1 ] ] + g [ i − j − 1 ] − i + 1   ( g [ i − j − 1 ] ! = 0 ) f[i] = f[g[i - j - 1]] + g[i - j - 1] - i + 1\ (g[i - j - 1] != 0) f[i]=f[g[ij1]]+g[ij1]i+1 (g[ij1]!=0)
f [ i ] = m a x ( i + 1 , j )   ( g [ i − j − 1 ] = 0 ) f[i] = max(i + 1, j) \ (g[i - j - 1] = 0) f[i]=max(i+1,j) (g[ij1]=0)
f [ i ] = f [ g [ i − j + 1 ] ] + g [ i − j + 1 ] − i   ( g [ i − j + 1 ] ! = 0 ) f[i] = f[g[i - j + 1]] + g[i - j + 1] - i\ (g[i - j + 1] != 0) f[i]=f[g[ij+1]]+g[ij+1]i (g[ij+1]!=0)
f [ i ] = m a x ( i , j + 1 ) ( g [ i − j + 1 ] = 0 ) f[i] = max(i, j + 1) (g[i - j + 1] = 0) f[i]=max(i,j+1)(g[ij+1]=0)

因为 A [ i ] ! = B [ j ] A[i] != B[j] A[i]!=B[j]的状态已经被合并了

那么剩下的状态就只剩下 A [ i ] = B [ j ] A[i] = B[j] A[i]=B[j]的了

所以剩下的转移就只有从 ( i − 1 , j ) (i - 1, j) (i1,j) ( i , j − 1 ) (i, j - 1) (i,j1)两种了

这样子直接做就差不多可以了

复杂度 Θ ( n ) \Theta(n) Θ(n)

Codes
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>

using namespace std;

const int N = 1e6 + 10;

__gnu_pbds::cc_hash_table<int, int> g;

int f[N], a[N], b[N], pos1[N], pos2[N], n;

int main() {
#ifdef ylsakioi
	freopen("eternal.in", "r", stdin);
	freopen("eternal.out", "w", stdout);
#endif
	scanf("%d", &n);
	for(int i = 1; i <= n; ++ i)
		scanf("%d", &a[i]), pos1[a[i]] = i;
	for(int i = 1; i <= n; ++ i)
		scanf("%d", &b[i]), pos2[pos1[b[i]]] = i;
	for(int i = n; i >= 1; -- i) {
		int j = pos2[i]; f[i] = INT_MAX;
		f[i] = min(f[i], g[i - j - 1] ? f[g[i - j - 1]] + (g[i - j - 1] - i + 1) : max(n - i + 2, n - j + 1));
		f[i] = min(f[i], g[i - j + 1] ? f[g[i - j + 1]] + (g[i - j + 1] - i) : max(n - i + 1, n - j + 2));
		g[i - j] = i;
	}
	//cout << g[0] << ' ' << f[g[0]] << endl;
	printf("%d\n", g[0] ? f[g[0]] + g[0] - 1: n);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值