zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    lua module
    lua require
    lua io
    lua table2
    lua table1
    【leetcode】魔术排列
    【leetcode】速算机器人
    【leetcode】黑白方格画
    【leetcode】根据数字二进制下 1 的数目排序
    【leetcode】插入区间
  • 原文地址:https://www.cnblogs.com/windysai/p/6824505.html
Copyright © 2011-2022 走看看