Codeforces 1453F Even Harder

题目链接

点我跳转

题目大意

给定 \(n\) 个关卡 , 第 \(i\) 个关卡有个权值为 \(ai\) 的传送门

当你在第 \(i\) 个关卡时 , 如果 \(ai = 0\) 并且 \(i!=n\) ,则你闯关失败

否则你可以跳到 \(i + 1\) ~ \(i + ai\) 中的任意关卡 ( \(ai + i <= n\) )

当你到达第 \(n\) 关时,闯关结束

但为了增加闯关难度,系统要求你删除最少的传送门( 即令 \(ai = 0\) )

使得从第一关开始,有且仅有一条可行的通关路线

解题思路

定义 \(dp[i][x]\) 为最小删除次数 , 使得从起点出发 , 只有一条可行路 , 这条可行路的终点为 \(i\)

\(i\) 点的上一个点 (设为 \(j\)) 最远能到达的位置是 \(x\) (其中 \(j + a[j] >= i\))

因为上一个点是 \(j\) , 且只有一条路

那么对于 \(k ∈[1,j-1]\) , 如果 \(k + a[k] >= i\) ,需要把 \(k\) 删除

对于 \(k ∈[j + 1 , i - 1]\) 如果 \(k + a[k] >= i\) ,需要把 \(k\) 删除

\(1\) ~ \(j-1\) 需要删除的 \(k\) 的个数为 \(pre\) , \(j+1\) ~ \(i-1\) 需要删除的 \(k\) 的个数为 \(suf\)

那么 $dp[i][j + a[j]] = pre + suf $

对于 \(suf\) 可以从 \(i - 1\)\(1\) 枚举,每次碰到一个 \(k + a[k] >= i,suf ++\)

对于 \(pre\) 它等于 \(min( dp[j][i - 1] , dp[j][i - 2] ... dp[j][1])\)

其含义如下:

最小删除次数

使得从起点出发,有一条可行路 , 这条可行路的终点为 \(j\)

\(j\) 点的上一个点 \(k\) 最远能到达的位置是 \(i-1、i-2、i-3 ... 1\) (即 \(a[k] + k < i\))

对于 \(pre\) 的转移如果需要枚举 \(i - 1 , i - 2 , ... , 1\) 那么就多了一层循环 , \(Tle\) 警告

而不难发现我们只需要找到最小的满足条件 \(a[k] + k < i\)\(dp[j][x] , x ∈[1 , i - 1]\)

所以我们可以维护一个前缀最小值 , 即 $dp[i][j] = min(dp[i][j] , dp[i][j - 1] $

那么显然 \(pre = dp[j][i - 1]\)

最后答案为 \(dp[n][n]\) (从起点开始只有一条可行路线 , 路的终点为 \(n\) , \(n\) 的上一个点最远能达到的距离为 \(n\))

AC_Code

#include<bits/stdc++.h>
using namespace std;
const int N = 3e3 + 10;
int n , a[N] , dp[N][N];
signed main()
{
	int T = 1;
	cin >> T;
	while(T --)
	{
		cin >> n;
		for(int i = 1 ; i <= n ; i ++) cin >> a[i];
		for(int i = 2 ; i <= n ; i ++) for(int j = 1 ; j <= n ; j ++) dp[i][j] = 2e9; //起点不能删除所以从 2 开始 
		for(int i = 2 ; i <= n ; i ++)
		{
			int suf = 0;
			for(int j = i - 1 ; j >= 1 ; j --)
			{
				if(j + a[j] >= i)
				{
					int pre = dp[j][i - 1];
					dp[i][j + a[j]] = min(dp[i][j + a[j]] , pre + suf);
					suf ++ ; 	
				} 
		 	}
			for(int j = i ; j <= n ; j ++) dp[i][j] = min(dp[i][j] , dp[i][j - 1]);
		}
		cout << dp[n][n] << '\n';
	}
	return 0;
}
posted @ 2020-12-06 08:35  GsjzTle  阅读(92)  评论(0编辑  收藏  举报