cf201.div1 Number Transformation II 【贪心】
1 题目描述:
被给一系列的正整数x1,x2,x3...xn和两个非负整数a和b,通过下面两步操作将a转化为b:
1.对当前的a减1。
2.对当前a减去a % xi (i=1,2...n)。
计算a转化到b最小需要的最短步数。
2 输入
第一行包含简单的整数n(1 ≤ n ≤ 10^5),第二行包含n个用空格分开的整数x1,x2,x3...xn(2 ≤ xi ≤ 10^9),第三行包含两个整数a和b(0 ≤ b ≤ a ≤ 10^9, a - b ≤ 10^6)。
3 输出
输出一个整数,a转化为b的最小步数。
4 思路
假设dp[k]表示从k+b转化到b所需要的最小步数,然后可以通过计算next=(b+k)-(b+k) % xi(i=1,2..n, next >= b)所能达到的最小值来计算dp[k],即dp[k]=dp[next-b]+1,这其实是一个贪心的选择,总是让每一步尽量产生大的跨度。为什么可以这样计算?由于dp[k]是一个递增函数。假设存在t,使得dp[k] = dp[k - t] + 1(k+b通过一步可以转化到k-t+b),如果t=1,显然dp[k-1]<dp[k];如果t > 1, 必然有dp[k-1] <= dp[k - t]+1,因为k+b可以通过一步转化为k-t+b,那么k-1+b也必然存在一步转化为k-t+b(只要k+b先-1转化为k+b-1,然后k+b-1必然就可以转化为k-t+b)。
5实现
1 #include <stdio.h>
2 #include <iostream> // std::cout
3 #include <algorithm> // std::sort
4 #include <vector> // std::vector
5 #include <functional>
6
7 #define min(a, b) ((a) < (b) ? (a) : (b))
8 int n, x[100005], a, b;
9
10 int main ( int argc, char *argv[] )
11 {
12 int i, t, tt, ans;
13
14 scanf("%d", &n);
15 for(i = 0; i < n; i++)
16 scanf("%d", &x[i]);
17 scanf("%d%d", &a, &b);
18 std::sort(x, x + n, std::greater<int>());
19 n = std::unique(x, x + n) - x;
20
21 ans = 0;
22 while (a != b)
23 {
24 tt = a - 1;
25 for (i = 0; i < n; i++)
26 {
27 t = a - (a % x[i]);
28 if (t < b)
29 x[i--] = x[--n];
30 else
31 tt = min(t, tt);
32 }
33 a = tt;
34 ans++;
35 }
36 printf("%d\n", ans);
37 return 0;
38 } /* ---------- end of function main ---------- */