Educational Codeforces Round 109 D题(DP)

D

​ 因为我太菜了,没看出这是一道DP,QAQ

状态表示 f [ i ] [ j ] f[i][j] f[i][j] 表示前 i i i 个 1 和前 j j j 个 0 匹配的最小代价。假设有 n 1 n_1 n1 个 1 和 n 2 n_2 n2 个0,那么最终的答案就是 f [ n 1 ] [ n 2 ] f[n_1][n_2] f[n1][n2]

状态转移:在求 f [ i ] [ j ] f[i][j] f[i][j] 时,考虑第 i i i 个 1 是否和第 j j j 个 0 匹配
​ 如果第 i i i 个1 和第 j j j 个 0 匹配,那么此时的代价就是 f [ i − 1 ] [ j − 1 ] + ∣ x [ i ] − b [ j ] ∣ f[i - 1][j - 1] + |x[i] - b[j]| f[i1][j1]+x[i]b[j],这里 x [ i ] x[i] x[i] 表示第 i i i 个 1 的位置, b [ j ] b[j] b[j] 表示第 j j j 个 0 的位置;

​ 如果第 i i i 个 1 和第 j j j 个 0 不匹配,这时候要求 f [ i ] [ j ] f[i][j] f[i][j],那么第 i i i 个 1 只能和前面的 0 进行匹配了,所以只能和第 k , k ∈ [ i , i + 1 , . . . , j − 1 ] k, k \in [i, i + 1, ..., j - 1] k,k[i,i+1,...,j1] 个 0 进行匹配,这里的范围比较重要

综上 i i i 个 1 是和第 k , k ∈ [ i , i + 1 , . . . , j ] k, k \in [i, i + 1, ..., j] k,k[i,i+1,...,j]个 0 进行匹配,所以得到
f [ i ] [ j ] = m i n ( f [ i − 1 ] [ k − 1 ] + ∣ x [ i ] − y [ k ] ∣ ) , k ∈ [ i , i + 1 , . . . , j ] f[i][j] = min(f[i - 1][k - 1] + |x[i] - y[k]|),k \in[i, i + 1, ..., j] f[i][j]=min(f[i1][k1]+x[i]y[k]),k[i,i+1,...,j]
​ 到了这一步,可以求出答案了,不过时间复杂度是 O ( n 3 ) O(n^3) O(n3) 的,因为需要枚举 i , j , k i, j, k i,j,k,所以需要进行优化

​ 我是把 k 前面几个和后面几个列出来,观察发现 f [ i ] [ j − 1 ] f[i][j - 1] f[i][j1] 的式子和这个很像:
f [ i ] [ j − 1 ] = m i n ( f [ i − 1 ] [ k − 1 ] + ∣ x [ i ] − y [ k ] ∣ ) , k ∈ [ i , i + 1 , . . . , j − 1 ] f[i][j - 1] = min(f[i - 1][k - 1] + |x[i] - y[k]|),k \in[i, i + 1, ..., j - 1] f[i][j1]=min(f[i1][k1]+x[i]y[k]),k[i,i+1,...,j1]
​ 然后 f [ i ] [ j − 1 ] f[i][j - 1] f[i][j1] f [ i ] [ j ] f[i][j] f[i][j]只相差了 k = j k = j k=j 的情况,所以可以得到:
f [ i ] [ j ] = m i n ( f [ i ] [ j − 1 ] , f [ i − 1 ] [ j − 1 ] + ∣ x [ i ] − y [ j ] ∣ ) f[i][j] = min(f[i][j - 1], f[i - 1][j - 1] + |x[i] - y[j]|) f[i][j]=min(f[i][j1],f[i1][j1]+x[i]y[j])
​ 完美的状态转移方程就完成了!

​ 初始化:当没有 1 的时候,是不需要任何代价的,所以 f [ 0 ] [ j ] , j ∈ [ 0 , n 2 ] = 0 f[0][j], j \in [0, n2] = 0 f[0][j],j[0,n2]=0,就可以了!

下面是AC代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N = 5e3 + 17;
int f[N][N];
int a[N], b[N];


signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int n, n1 = 0, n2 = 0;
	cin >> n;
	for(int i = 1; i <= n; ++i){
		int x;
		cin >> x;
		if(x) a[++n1] = i;
		else b[++n2] = i;
	}
	memset(f, 0x3f, sizeof f);
	for(int i = 0; i < N; ++i){
		f[0][i] = 0;
	}
	for(int i = 1; i <= n1; ++i){
		for(int j = 1; j <= n2; ++j){
			f[i][j] = min(f[i][j - 1], f[i - 1][j - 1] + abs(a[i] - b[j]));
		}
	}
	cout << f[n1][n2];
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值