codeforces 798C.Mike and gcd problem 解题报告

题目意思:给出一个n个数的序列:a1,a2,...,an (n的范围[2,100000],ax的范围[1,1e9] )

现在需要对序列a进行若干变换,来构造一个beautiful的序列: b1,b2, ..., bn,使得最大公约数 gcd(b1,b2,...,bn) > 1。

变换:  任意ai,ai+1 进行一次操作时,可以用 ai-ai+1, ai+ai+1 来替换。

问序列 a 构造成 序列 b ,使得gcd(b序列) > 1 的最小操作次数

题目解析:

  首先,这个题目是肯定有解的,也就是恒输出yes

     试想一下,相邻两个数之间无非就是四种情况:

     (1)对于同偶情况,不需要做转换,公约数直接为2;

     (2)对于同奇情况,只需要变换一次,两奇数进行加减操作,最终结果是偶数,公约数此时为2

  (3)一奇一偶,变换两次: ai, ai+1 ——》 ai-ai+1, ai+ai+1  ——》2(ai+1,ai) ——》 公约数为2

  此时问题就转化成: 构造一个序列所有数的公约数为2的最少操作次数。

      当然,在处理序列之前,要先判断整个序列是否已经有公约数了(注意,并不一定为2); 如果有,代表已经符合条件:gcd(b1,b2,...,bn) > 1,直接输出0即可。(不需要对序列a进行任何操作。

两种方法

方法一 :贪心+数论

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cstring>
 5 using namespace std;
 6 
 7 const int maxn = 1e5 + 5;
 8 int a[maxn];
 9 
10 int GCD(int b1, int b2)
11 {
12     if (b2 == 0)
13         return b1;
14     return GCD(b2, b1%b2);
15 }
16 
17 int main()
18 {
19     #ifndef ONLINE_JUDGE
20         freopen("in.txt", "r", stdin);
21     #endif // ONLINE_JUDGE
22 
23     int n;
24     while (scanf("%d", &n) !=EOF) {
25        scanf("%d", &a[0]);
26        int t=a[0], cnt = 0;
27        for (int i = 1; i < n; i++) {
28         scanf("%d", &a[i]);
29         t = GCD(t, a[i]);
30         if (t > 1) { cnt++; }
31        }
32        printf("YES
");
33        if ( cnt == n-1 ) { printf("0
"); }  // all is even
34        else {
35             // scan two times;
36             int ans = 0;
37             for (int i = 0; i < n; i++) {
38                 if (a[i]%2 && a[i+1]%2 && i+1 < n) {  // two odd
39                     ans += 1;
40                     a[i] = 2;
41                     a[i+1] = 2;
42                 }
43             }
44 
45             for (int i = 0; i < n; i++) {
46                 if (i+1 < n && (a[i]%2 && a[i+1]%2 == 0)|| (a[i]%2 == 0 && a[i+1]%2) ) {   // one odd one even
47                     ans += 2;
48                     a[i+1] = 2;
49                 }
50             }
51             printf("%d
", ans);
52        }
53     }
54     return 0;
55 }
View Code

方法二:动态规划(参考网上的,dp是我的痛~  = =)

设:

dp[i][0]: 前i-1个数为偶数,第i个数为偶数的最少操作次数
dp[i][1]:  前i-1个数为偶数,第i个数为奇数的最少操作次数
如果第 i 个数是奇数,
dp[i][0] = min(dp[i-1][0]+2, dp[i-1][1]+1);

dp[i][1] = min(dp[i-1][0], inf);

如果第 i 个数是偶数,
dp[i][0] = min(dp[i-1][0], dp[i-1][1]+2);
dp[i][1] = inf;

还有一个初始化的问题需要注意下:
dp[0][!(a[0]%2)] = inf;      ——》 这个要细心体会下
假设序列中第一个数就是偶数,dp[0][0]= 0
dp[0][1]= inf
假设序列中第一个数就是奇数,dp[0][0]= inf   dp[0][1]= 0
 
 
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cstring>
 5 using namespace std;
 6 
 7 const int inf = 10000000;
 8 const int maxn = 1e5 + 5;
 9 int a[maxn];
10 // dp[i][0]: 前i-1个数为偶数,第i个数为偶数的最少操作次数
11 // dp[i][1]:  前i-1个数为偶数,第i个数为奇数的最少操作次数
12 int dp[maxn][2];
13 
14 int GCD(int b1, int b2)
15 {
16     if (b2 == 0)
17         return b1;
18     return GCD(b2, b1%b2);
19 }
20 
21 int main()
22 {
23     #ifndef ONLINE_JUDGE
24         freopen("in.txt", "r", stdin);
25     #endif // ONLINE_JUDGE
26 
27     int n;
28     while (scanf("%d", &n) !=EOF) {
29        scanf("%d", &a[0]);
30        int t=a[0], cnt = 0;
31 
32        for (int i = 1; i < n; i++) {
33             scanf("%d", &a[i]);
34             t = GCD(t, a[i]);
35             if (t > 1) { cnt++; }
36        }
37        printf("YES
");
38        if ( cnt == n-1 ) { printf("0
"); }  // all 有公约数
39 
40        else {
41             memset(dp, 0, sizeof(dp));
42             dp[0][!(a[0]%2)] = inf;
43 
44             for (int i = 1; i < n; i++) {
45                 if (a[i]%2) {     // odd
46                     dp[i][0] = min(dp[i-1][0]+2, dp[i-1][1]+1);
47                     dp[i][1] = min(dp[i-1][0], inf);
48                 }
49                 else {
50                     dp[i][0] = min(dp[i-1][0], dp[i-1][1]+2);
51                     dp[i][1] = inf;
52 
53                 }
54             }
55             printf("%d
", dp[n-1][0]);
56        }
57     }
58     return 0;
59 }
View Code
原文地址:https://www.cnblogs.com/windysai/p/6824505.html