• 2017 ACM/ICPC Asia Regional Shenyang Online(部分题解)


    HDU 6197 array array array

    题意

    输入n和k,表示输入n个整数和可以擦除的次数k,如果至多擦除k次能是的数组中的序列是不上升或者是不下降序列,就是魔力数组,否则不是。

    解题思路

    分别求最长不下降和不上升子序列的长度,看不符合要求的数字和k的大小。

    这里使用优化后的求解最长不上升和不下降子序列的算法。

     1 #include <cstdio>
     2 #include <algorithm>
     3 using namespace std;
     4 
     5 const int maxn = 100010;
     6 const int inf = 99999999;
     7 int A[maxn], B[maxn], g[maxn], d[maxn];
     8 int n, t;
     9 
    10 int main()
    11 {
    12     int T;
    13     scanf("%d", &T);
    14     while(T--) {
    15         scanf("%d%d", &n, &t);
    16         for(int i = 0; i < n; i++) {
    17             scanf("%d", &A[i]);
    18         }
    19         
    20         for(int i = 1; i <= n; i++) {
    21             g[i] = inf;
    22         }
    23         for(int i = 0; i < n; i++) {
    24             int k = upper_bound(g + 1, g + n + 1, A[i]) - g;
    25             d[i] = k;
    26             g[k] = A[i];
    27         }
    28         int s1 = 0;
    29         for(int i = 0; i < n; i++)
    30             if(s1 < d[i])
    31                 s1 = d[i];
    32         
    33         int j = 0;
    34         for(int i = n - 1; i >= 0; i--) {
    35             B[j++] = A[i];
    36         }
    37         for(int i = 1; i <= n; i++) {
    38             g[i] = inf;
    39         }
    40         for(int i = 0; i < n; i++) {
    41             int k = upper_bound(g + 1, g + n + 1, B[i]) - g;
    42             d[i] = k;
    43             g[k] = B[i];
    44         }
    45         int s2 = 0;
    46         for(int i = 0; i < n; i++)
    47             if(s2 < d[i])
    48                 s2 = d[i];
    49         
    50         //printf("%d %d
    ",s1,s2);
    51         int ans = max(s1, s2);//最长上升或者下降子序列,取大值意味者修改更少 
    52         if(n - t <= ans)
    53             printf("A is a magic array.
    ");
    54         else
    55             printf("A is not a magic array.
    ");
    56     }
    57     return 0;
    58 }

    HDU 6195 cable cable cable

    题意

    给出n个屏幕和k个光源,要求的是从n个屏幕中任意的k个屏幕显示不同颜色的光源至少需要几根电缆

    解题思路

    有两个出发点

    从屏幕出发,前k个屏幕都单独和k个光源连接,剩下n-k个屏幕应该和每个光源都有连接,否则随意挑的时候,可能会出现重复。故总的电缆数是k+(n-k)*k。

    从光源出发,每个光源至少应该和(n-k+1)个屏幕相连,这样选出的k个屏幕才能可以组成k种不同的光源点。故总的电缆数是(n-k+1)* k。

     1 #include <cstdio>
     2 
     3 int main()
     4 {
     5     long long n, k;
     6     while(scanf("%lld%lld", &n, &k) != EOF) {
     7         printf("%lld
    ", k *( n - k + 1));
     8     }
     9     return 0;
    10 } 

    HDU 6198 number number number

    题意

    首先定义了斐波那契数列F0 = 0,F1 = 1;

                 F= Fn - 1 + Fn - 2(n>=2).

    给出一个k,如果一个正整数能够由连续(可重复)的斐波那契数加和得到,就是一个好数,否则就是一个坏数。现在给出一个k,问最小的坏数是多少。

    解题思路

    首先应该想到的应该是当k = 1 时,最小的坏数是5 - 1 = 4;

              当k = 2 时,最小的坏数是13 - 1 = 12;

              当k = 3 时,最小的坏数是34 - 1 = 33;

    然后如果拿5为斐波那契数的第一项,8位第二项,可以发现规律就是对于输入的k,答案是第f(2*k+1)项斐波那契数 - 1。

    不过题中给出的k的范围是(1<=k<=10^9),最大可能是第2*10^9项斐波那契数。就需要矩阵快速幂计算某一项斐波那契数了。代码如下:

     1 #include <cstdio>
     2 #include <cstring>
     3 typedef long long ll;
     4 const int mod = 998244353;
     5 struct Matrix {
     6     ll x[2][2];
     7 };
     8 Matrix Mmul(Matrix a, Matrix b) {
     9     Matrix tmp;
    10     memset(tmp.x, 0, sizeof(tmp.x));
    11     for(int i = 0; i < 2; i++) {
    12         for(int j = 0; j < 2; j++) {
    13             for(int k = 0; k < 2; k++) {
    14                 tmp.x[i][j] = (tmp.x[i][j] + a.x[i][k] * b.x[k][j] % mod) % mod;
    15             }
    16         }
    17     }
    18     return tmp;
    19 }
    20 Matrix Mqpow(Matrix a, ll n) {
    21     Matrix tmp;
    22     for(int i = 0; i < 2; i++) {
    23         for(int j = 0; j < 2; j++) {
    24             tmp.x[i][j] = i == j ? 1 : 0;
    25         }
    26     }
    27     
    28     while(n) {
    29         if(n&1)
    30             tmp = Mmul(tmp, a);
    31         a = Mmul(a, a);
    32         n >>= 1;
    33     }
    34     return tmp;
    35 }
    36  int main()
    37 {
    38     ll k;
    39     while(scanf("%lld", &k) != EOF) {
    40         Matrix st;
    41         st.x[0][0] = 1; st.x[0][1] = 1;
    42         st.x[1][0] = 1; st.x[1][1] = 0;
    43         
    44         Matrix init;
    45         init.x[0][0] = 1; init.x[0][1] = 0;
    46         init.x[1][0] = 1; init.x[1][1] = 0;
    47         
    48         st = Mqpow(st, 2 * k + 1);
    49         st = Mmul(st, init);
    50         /*for(int i = 0; i < 2; i++) {
    51             for(int j = 0; j < 2; j++) {
    52                 printf("%lld ", st.x[i][j]);
    53             }
    54             puts("");
    55         }*/
    56         printf("%lld
    ", (st.x[0][0] - 1 + mod) % mod);
    57     }    
    58     return 0;    
    59 }

    HDU 6201 transaction transaction transaction

    题意

    给出由n-1条边联通的n个顶点的图(可以将其方便的看为一棵树),每个顶点都有卖一本书的价格,但是每个地方都不同,一个商人想从中赚取差价,但是点与点之间还有路费,问这个人最多能赚取多少钱?

    解题思路

    先将其转换为一棵树来处理,定义两种状态分别是:dp[u][0] 表示从u及其子孙结点中的一点买书(包括所花的路费)的最小值(由于是负数,所以也即求最大值)

                                                                      dp[u][1]表示从u及其子孙结点中的一点卖书(包括所花的路费)的最大值

    那么从u结点及其子孙结点买再卖给u及其子孙结点的利润,也就是一个结点的利润是dp[u][0] +dp[u][1]。最后的答案就是取所有结点的最大值即可。代码如下:

     1 #include <cstdio>
     2 #include <vector>
     3 #include <algorithm>
     4 using namespace std;
     5 const int maxn = 100000 + 10;
     6 
     7 struct Node {
     8     int v, w;
     9     Node(int _v, int _w):v(_v),w(_w){};
    10 };
    11 
    12 vector<Node> vec[maxn];
    13 int val[maxn];
    14 int n, ans;
    15 int dp[maxn][2];
    16 
    17 void dfs(int u, int pre) {
    18     dp[u][0] = -val[u];//从u结点及其子孙结点中的一点买书所花的最少的钱(一定是一个负数) 
    19     dp[u][1] = val[u]; //从u结点及其子孙结点中的一点卖书所赚的最多的钱 
    20     for(int i = 0; i < vec[u].size(); i++) {
    21         int v = vec[u][i].v;
    22         int w = vec[u][i].w;
    23         if(v == pre) continue;
    24         dfs(v, u);
    25         //当前结点(买书)和它的子结点(买书)“加上”路费的值 谁更大(都是负数) 
    26         dp[u][0] = max(dp[u][0], dp[v][0] - w); 
    27         //当前结点(卖书)和它的子结点(卖书)减去路费的值 谁更大 
    28         dp[u][1] = max(dp[u][1], dp[v][1] - w);
    29     }
    30     ans = max(ans, dp[u][0] + dp[u][1]);
    31 }
    32 
    33 int main()
    34 {
    35     int T;
    36     scanf("%d", &T);
    37     while(T--) {
    38         scanf("%d", &n);
    39         for(int i = 1; i <= n; i++) {
    40             scanf("%d", &val[i]);
    41             vec[i].clear();
    42         }                
    43         for(int i = 1; i <= n - 1; i++) {
    44             int u, v, w;
    45             scanf("%d%d%d", &u, &v, &w);
    46             vec[u].push_back(Node(v, w));
    47             vec[v].push_back(Node(u, w));
    48         }
    49         ans = 0;
    50         dfs(1, -1);
    51         printf("%d
    ", ans);
    52     }    
    53     return 0;
    54 }

     HDU 6205 card card card

    题意

    给出n摞牌,排成一行,然后n个数表示每摞有多少张牌,还给出每一摞牌的罚值,我们可以通过将第一摞移动到最后来改变次数,使得拿更多的牌,拿取一摞的规则是一张一张的拿到手中并且使他们面朝上,拿完之后翻转一定数目的牌使之朝下,如果能够翻转的牌数等于该摞牌的罚值,表示可以继续往后拿,直至拿完。问最少移动几次使得拿取最多的牌。

    解题思路

    问循环移动的次数,我们先将其复制一遍到它的后面,然后通过最大子段和的思想,每次选取满足条件的最大子段,不同的是限制的条件是a[i]-b[i]的差要大于等于0,而结果是所有牌的数目,所以需要分开计算。代码如下:

     1 #include <cstdio>
     2 const int inf = 99999999;
     3 const int maxn = 2000000 + 10;
     4 int a[maxn], b[maxn];
     5 
     6 int main()
     7 {
     8     int n;
     9     while(scanf("%d", &n) != EOF) {
    10         for(int i = 1; i <= n; i++) {
    11             scanf("%d", &a[i]); 
    12             a[i + n] = a[i];
    13         }
    14         int x;
    15         for(int i = 1; i <= n; i++) {
    16             scanf("%d", &x); 
    17             b[i] = a[i] - x;
    18             b[i + n] = b[i];
    19         }
    20         
    21         int sum1 = 0, sum2 = 0;
    22         int ans = 0;
    23         int l = 0, r = 0, tmp = 1;
    24         for(int i = 1; i <= 2 * n; i++) {
    25             sum1 += a[i];
    26             sum2 += b[i];
    27             if(sum1 > ans) {
    28                 ans = sum1;
    29                 l = tmp;
    30                 r = i;
    31             }
    32             if(sum2 < 0) {
    33                 sum1 = 0;
    34                 sum2 = 0;
    35                 tmp = i + 1;
    36                 if(tmp > n + 1)
    37                     break;
    38             }
    39         }
    40         printf("%d
    ", l-1); 
    41     }    
    42     return 0;
    43 } 
  • 相关阅读:
    easyui的datagrid右侧没有边框线
    移除input在type="number"时的上下箭头
    端口被占用的解决办法
    给DOM操作生成的元素添加事件
    前端工具——Gulp篇
    python类型学习
    python对象学习
    Python之系统交互(subprocess)
    如何准确高效的获取数据库新插入数据的主键id
    接口和抽象类有什么区别
  • 原文地址:https://www.cnblogs.com/wenzhixin/p/9817825.html
Copyright © 2020-2023  润新知