zoukankan      html  css  js  c++  java
  • agc032

    T1 题意:你要进行n次操作,第i次选择一个数k∈[1,i],并插入到当前序列的第k个位置。给定目标序列,输出操作序列。100,2s。

    解:冷静分析一波,我们可以从后往前确定操作序列。这样每次确定一个操作之后就会删除一个数。

    如果有ai = i的位置那我们显然可以把这个i操作放到操作序列最后。如果有多个这样的i,从后往前处理。

     1 #include <bits/stdc++.h>
     2 
     3 const int N = 110;
     4 
     5 int a[N], b[N];
     6 
     7 int main() {
     8 
     9     int n;
    10     scanf("%d", &n);
    11     for(int i = 1; i <= n; i++) {
    12         scanf("%d", &b[i]);
    13     }
    14     for(int i = n; i >= 1; i--) {
    15         bool f = 0;
    16         for(int j = i; j >= 1; j--) {
    17             if(b[j] > j) break;
    18             if(b[j] == j) {
    19                 a[i] = j;
    20                 //printf("a %d = %d 
    ", i, j);
    21                 for(int k = j; k < i; k++) {
    22                     b[k] = b[k + 1];
    23                 }
    24                 f = 1;
    25                 break;
    26             }
    27         }
    28         if(!f) {
    29             printf("-1");
    30             return 0;
    31         }
    32     }
    33     for(int i = 1; i <= n; i++) {
    34         printf("%d
    ", a[i]);
    35     }
    36     return 0;
    37 }
    AC代码

    T2 题意:构建一个n个点的简单无向连通图,使得每个点的邻居的编号之和相等。100,2s。

    解:先玩一下3和4的时候,发现是一张完全二分图,且左右部的节点编号之和相等。

    这启发我们把这些点分成k份,每份的编号之和相同。然后每个点向其他所有不是同类的点连边。

    n为偶数的时候就是首尾配对,和为n + 1,n为奇数的时候提出n来然后首尾配对,和为n。

     1 #include <bits/stdc++.h>
     2 
     3 const int N = 110;
     4 
     5 int vis[N];
     6 
     7 int main() {
     8 
     9     int n, cnt1 = 0;
    10     scanf("%d", &n);
    11     int t = (n + 1) * n / 2;
    12     if(t & 1) {
    13         if((n & 1) == 0) {
    14             printf("%d
    ", n * (n - 2) / 2);
    15             for(int i = 1; i <= n; i++) {
    16                 for(int j = i + 1; j <= n; j++) {
    17                     if(i + j != n + 1) {
    18                         printf("%d %d 
    ", i, j);
    19                     }
    20                 }
    21             }
    22             return 0;
    23         }
    24         else {
    25             printf("%d 
    ", (n - 1) * (n - 3) / 2 + n - 1);
    26             for(int i = 1; i < n; i++) {
    27                 printf("%d %d 
    ", i, n);
    28             }
    29             for(int i = 1; i < n; i++) {
    30                 for(int j = i + 1; j < n; j++) {
    31                     if(i + j != n) {
    32                         printf("%d %d 
    ", i, j);
    33                     }
    34                 }
    35             }
    36         }
    37         return 0;
    38     }
    39     t /= 2;
    40     for(int i = n; i >= 1 && t; i--) {
    41         if(t >= i) {
    42             t -= i;
    43             vis[i] = 1;
    44             cnt1++;
    45         }
    46     }
    47     printf("%d
    ", cnt1 * (n - cnt1));
    48     for(int i = 1; i <= n; i++) {
    49         if(vis[i]) {
    50             for(int j = 1; j <= n; j++) {
    51                 if(!vis[j]) {
    52                     printf("%d %d 
    ", i, j);
    53                 }
    54             }
    55         }
    56     }
    57 
    58     return 0;
    59 }
    AC代码

    T3 题意:给定简单无向连通图,问是否能把所有边分成三个环。点可以重复经过。10w,10w,2s。

    解:这TM居然是分类讨论...首先有度数为奇的肯定不行。然后只要排除一种情况:总环数 < 3即可。

    因为如果环比较多,任意两个相邻(共用某一点)的环肯定能合二为一的......

    如果有某个点的度数大于4,肯定合法。如果没有点的度数大于2,肯定不合法。

    当点的度数最大为4的时候,可以把其他所有度数为2的点缩成某些边。

    如果只有一个点的度数为4,相当于该点有两条自环,不行。

    如果有两个点的度数为4,有一种特殊情况不合法:

    其余情况和有大于2个点的度数等于4的时候,均合法。

     1 #include <bits/stdc++.h>
     2 
     3 const int N = 100010;
     4 
     5 struct Edge {
     6     int nex, v;
     7 }edge[N << 1]; int tp = 1;
     8 
     9 int e[N], n, m, in[N], vis[N], stk[N], top, cnt, A, B;
    10 
    11 inline void add(int x, int y) {
    12     tp++;
    13     edge[tp].v = y;
    14     edge[tp].nex = e[x];
    15     e[x] = tp;
    16     return;
    17 }
    18 
    19 void DFS(int x, int f) {
    20     if(f && x == A) {
    21         puts("Yes");
    22         exit(0);
    23     }
    24     else if(x == B) return;
    25     for(int i = e[x]; i; i = edge[i].nex) {
    26         int y = edge[i].v;
    27         if(y != f) DFS(y, x);
    28     }
    29     return;
    30 }
    31 
    32 int main() {
    33     scanf("%d%d", &n, &m);
    34     for(int i = 1, x, y; i <= m; i++) {
    35         scanf("%d%d", &x, &y);
    36         add(x, y);
    37         add(y, x);
    38         in[x]++;
    39         in[y]++;
    40     }
    41     int largeIn = 0, cnt = 0;
    42     for(int i = 1; i <= n; i++) {
    43         if(in[i] & 1) {
    44             puts("No");
    45             return 0;
    46         }
    47         if(largeIn < in[i]) {
    48             largeIn = in[i];
    49             cnt = 1;
    50         }
    51         else if(largeIn == in[i]) {
    52             cnt++;
    53         }
    54     }
    55     if(largeIn > 4) {
    56         puts("Yes");
    57         return 0;
    58     }
    59     else if(largeIn == 4 && cnt > 2) {
    60         puts("Yes");
    61         return 0;
    62     }
    63     else if(largeIn == 4 && cnt == 2) {
    64         for(int i = 1; i <= n; i++) {
    65             if(in[i] == 4) {
    66                 if(!A) A = i;
    67                 else {
    68                     B = i;
    69                     break;
    70                 }
    71             }
    72         }
    73         DFS(A, 0);
    74     }
    75     puts("No");
    76     return 0;
    77 }
    AC代码

    T4 题意:给定一个排列。你可以花费A使一个区间最左边的数跑到最右边,其余区间内的数左移。也可以花费B来进行逆操作。求使其变成升序的最小代价。5000,2s。

    解:神仙DP。

    显然有个n3的区间DP是设f[l][r]表示把[l, r]这一段排序。转移的时候一段区间可以由两个子区间拼起来,也可以找到其中最值然后挪一次。

    正解全然不同......我们只注重这些元素的相对位置,也就是说下标可以为实数。

    然后考虑每次操作等价于把一个数往旁边挪,别的数不变。

    然后考虑最优解肯定是若干个数往左,若干个数往右,若干个数不动。我们以那些不动的数来DP。

    设f[i][j]表示值域前i个数全部排好序了,且最大的那个不动的数是j,且比j大的数全部聚集在(j, j + 1)这一段的最小代价。

    考虑f[i][j]是怎么得来的:如果当前这个数i初始时在j的左边,那么一定要往右移(i比j大)。反之一定要往左移(聚集到(j, j + 1)中)。

    然后我们忽了一种转移:i在j右边的时候也可以不移动!此时f[i][i] = f[i - 1][j]

    然后就完事了。

     1 #include <bits/stdc++.h>
     2 
     3 typedef long long LL;
     4 const int N = 5010;
     5 
     6 LL f[N][N];
     7 int a[N], p[N];
     8 
     9 int main() {
    10 
    11     int n;
    12     LL A, B;
    13     scanf("%d%lld%lld", &n, &A, &B);
    14     for(int i = 1; i <= n; i++) {
    15         scanf("%d", &a[i]);
    16         p[a[i]] = i;
    17     }
    18     memset(f, 0x3f, sizeof(f));
    19     /// DP
    20     f[0][0] = 0;
    21     for(int i = 1; i <= n; i++) {
    22         for(int j = 0; j < i; j++) {
    23             /// f[i][j]
    24             /*for(int k = 0; k < i; k++) {
    25                 /// f[i][j] <- f[i - 1][k]
    26                 if(p[i] < p[k]) {
    27                     f[i][j] = std::min(f[i][j], f[i - 1][k] + A);
    28                 }
    29                 else {
    30                     f[i][j] = std::min(f[i][j], f[i - 1][k] + B);
    31                 }
    32             }*/
    33             if(p[i] < p[j]) {
    34                 f[i][j] = f[i - 1][j] + A;
    35             }
    36             else {
    37                 f[i][j] = f[i - 1][j] + B;
    38                 f[i][i] = std::min(f[i][i], f[i - 1][j]);
    39             }
    40         }
    41         /*for(int j = 0; j <= i; j++) {
    42             printf("%3lld ", f[i][j]);
    43         }
    44         puts("");*/
    45     }
    46 
    47 
    48     LL ans = 4e18;
    49     for(int i = 0; i <= n; i++) {
    50         ans = std::min(ans, f[n][i]);
    51     }
    52     printf("%lld
    ", ans);
    53     return 0;
    54 }
    AC代码

    T5 题意:给定序列,把它们两两配对使得每一对的和 % MO的最大值最小。10w,1e9,2s。

    解:排序之后考虑最优方案长什么样。

    然后发现它们一定长这样...因为不满足这样的方案调整成这样一定更优。

    枚举分界点是n2的,但是发现分界点越靠左越优,于是二分这个分界点,使其满足条件(左边的和全小于MO,右边全不小于MO),然后一次得出答案。

    单调性:就考虑若分界点在a和b都满足条件,那么a,b之间的任一点也满足条件。比b小所以左边满足,比a大所以右边满足。

     1 #include <bits/stdc++.h>
     2 
     3 const int N = 200010;
     4 
     5 int a[N], MO, n;
     6 
     7 inline bool check(int p) {
     8     int l = p + 1, r = n;
     9     while(l < r) {
    10         if(a[l] + a[r] < MO) return false;
    11         l++;
    12         r--;
    13     }
    14     return true;
    15 }
    16 
    17 int main() {
    18     scanf("%d%d", &n, &MO);
    19     n <<= 1;
    20     for(int i = 1; i <= n; i++) {
    21         scanf("%d", &a[i]);
    22     }
    23     std::sort(a + 1, a + n + 1);
    24 
    25     int l = n, r = n; /// [1, l] (l, r]
    26     while(a[n] + a[l - 1] >= MO && l >= 2) l -= 2;
    27     while(a[1] + a[r] >= MO && r >= 2) r -= 2;
    28     l /= 2;
    29     r /= 2;
    30 
    31     while(l < r) {
    32         int mid = (l + r) >> 1;
    33         if(check(mid * 2)) r = mid;
    34         else l = mid + 1;
    35     }
    36 
    37     int p = r * 2;
    38     int ans = 0;
    39     l = 1, r = p;
    40     while(l < r) {
    41         ans = std::max(ans, a[l] + a[r]);
    42         l++;
    43         r--;
    44     }
    45     l = p + 1, r = n;
    46     while(l < r) {
    47         ans = std::max(ans, (a[l] + a[r]) % MO);
    48         l++;
    49         r--;
    50     }
    51     printf("%d
    ", ans);
    52     return 0;
    53 }
    AC代码
  • 相关阅读:
    第1章 游戏之乐——NIM(3)两堆石头的游戏
    NFS介绍和安装
    shell脚本删除N天前的目录-----附linux和mac上date命令的不同
    关于子线程更新UI
    WIN7下安装SVNserver端及client搭建协作环境
    Selenium_WebDriver登录模拟鼠标移动切换窗体等操作练习(cssSelector初练手)_Java
    UICollectionView具体解释
    局域网使用的IP地址范围
    hdu 2955 Robberies
    POJ 1661 Help Jimmy(DP,注意边界)
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/10683361.html
Copyright © 2011-2022 走看看