ZR1012 Zbox loves keyboard (dp)

Description

文本框中有 x x x 个字符。每两秒可以进行全选、复制、粘贴。每一秒可以进行输入、退格。求多少秒才能让文本框有 n n n 个字符?

1 ≤ n , x ≤ 1 0 6 1 \leq n, x \leq 10^6 1n,x106

Solution

若没有退格一项,则为一道非常水的 dp 题。其中全选再粘贴的操作没有必要。

若加入退格,发现退格只有两个,全部删或删一个。全部删只用一次,而且在开头用。删一个删的少,而且只在开头或结尾用。若当前为 S S S,目标为 T T T。从 S S S 开始周期的全选复制粘贴。大约需 l o g 2 ( S − T ) × 4 log_2(S-T) \times 4 log2(ST)×4 次。保守估计可以知道退格键按了超过 100 100 100 次一定不更优,所以对于退格只进行 100 100 100 内的转移即可。对于边界也类似考虑。

f i f_i fi i i i 个字符至少多少秒,则有输入字符,百内退格,全选复制后一直粘贴三种转移。

Code

#include 
using namespace std;
typedef long long ll;
const int N = 2e6 + 5;
int read() {
	int x = 0, f = 0; char ch = 0;
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
int f[N]; 
int main(){
	memset(f, 0x7f, sizeof(f));
	int x = read(), n = read();
	f[x] = 0, f[0] = min(3, x);
	int m = max(x, n) + 100;
	for (int i = 1; i <= m; i++) {
		f[i] = min(f[i], f[i - 1] + 1);
		for (int j = 1; j <= 100; j++) f[i] = min(f[i], f[i + j] + j);
		for (int j = 2; i * j <= m; j++) f[i * j] = min(f[i * j], f[i] + 4 + (j - 1) * 2);
	} 
	printf("%d\n", f[n]);
	return 0;
}

你可能感兴趣的:(动态规划)