zoukankan      html  css  js  c++  java
  • 比赛-某训练赛 Round1 (Aug, 2017)

    题目依次为 NKOJ 上 P3496 P4236 P3774 P2407

    1.数三角形

    方法很多, 比如推出三边 x y z 的限制关系, 然后加加减减得到计算式子

    不过也可以用观察法, 暴力计算出 n 为 1 至 13 对应的结果为:

    0  0  0  1  3  7  13  22  34  50  70  95  125

    相邻两数差为:

      0  0  1  2  4  6  9  12  16  20  25  30

    这些相邻两数相邻差又为:

        0  1  1  2  2  3  3  4  4  5  5

    找到规律了, 如果结果第 i 项为第 i - 1 项的值加上 plus , 则每次 plus 增加的值为 (i / 2 - 1)

    比如结果第 7 项值为 13 , 上一项值为 7 , 此时 plus 为 6 , 上一次的 plus 为 4 , 两个 plus 的差正正好为 (7 / 2 - 1) = 2

    写出递推算法,时间复杂度 O(n) .

     1 #include <stdio.h>
     2 
     3 long long int ans, plus;
     4 int n;
     5 
     6 int main()
     7 {
     8     int i;
     9     scanf("%d", &n);
    10     for (i = 4; i <= n; ++i)
    11         ans += plus += (i >> 1) - 1;
    12     printf("%lld
    ", ans);
    13     return 0;
    14 }
    P1

    2.翻硬币

    SG(i) 表示硬币在左起第 i 位的局面

    0 对应 SG 的值为 0, SG(0) = 0

    1 后续局面为 0 , 所以 SG(1) = mex(SG(0)) = 1

    01后续局面为 00, 10, 所以 SG(2) = mex(SG(0), SG(1)) = 2

    001 后续局面为 000, 100, 010, 所以 SG(3) = mex(SG(0), SG(1), SG(2)) = 3

    0001 后续局面为 1000, 0100, 0010, 所以 SG(4) = mex(SG(1), SG(2), SG(3)) = 4

    ...

    推导出 SG(i) = i mod 5

    将正面朝上的硬币 SG 值按位异或就可以得出总的 SG 值了

     1 #include <stdio.h>
     2 
     3 int n, k, ans;
     4 
     5 int main()
     6 {
     7     int i, j;
     8     scanf("%d", &k);
     9     while (k--) {
    10         ans = 0;
    11         scanf("%d", &n);
    12         for (i = 1; i <= n; ++i) {
    13             scanf("%d", &j);
    14             if (j)
    15                 ans ^= i % 5;
    16         }
    17         if (ans)
    18             printf("Yes
    ");
    19         else
    20             printf("No
    ");
    21     }
    22     return 0;
    23 }
    P2

    3.小鸟

    动态规划, 用单调队列优化

    f[i] 表示停留在第 i 棵树上的最小疲劳值, 可以得到方程:

    f[i] = min{ f[j] + h[j]>=h[i]?1:0 }

    i 的范围是 [1, n], j 的范围是 [i-k, i-1]

    裸动规的时间复杂度是 O(nk) , 可以过 60% 的数据, 用单调队列优化后时间复杂度是 O(n)

    构造一个根据 f 值递增的单调队列, 记录树的编号, 当队首对应树的编号小于 i - k 时说明无法飞到第 i 棵树, 将队首出队;

    操作之后的队首对应的就是可以转移到 f[i] 的最小 f[j] 值, 再根据高度判断疲劳值是否 +1, 然后将 f[i] 对应树的编号在保

    持单调性的情况下入队

    注意, 为了使 f[i] 尽量小, 即疲劳值尽量不增加, 应该使第 j 棵树的高度尽量高, 因此单调队列还应该在 f 相同时把 d (即树

    高度)较大的放在队首

     1 #include <stdio.h>
     2 
     3 int n, q, k;
     4 int d[500005], f[500005], que[500005];
     5 
     6 int main()
     7 {
     8     int i, j, head, tail;
     9     scanf("%d", &n);
    10     for (i = 1; i <= n; ++i)
    11         scanf("%d", &d[i]);
    12     scanf("%d", &q);
    13     for (i = 1; i <= q; ++i) {
    14         scanf("%d", &k);
    15         f[1] = 0;
    16         head = tail = 1;
    17         que[tail++] = 1;
    18         for (j = 2; j <= n; ++j) {
    19             while (j - k > que[head])
    20                 ++head;
    21             f[j] = f[que[head]] + (d[que[head]] <= d[j]);
    22             while (head < tail && (f[j] < f[que[tail - 1]] || f[j] == f[que[tail - 1]] && d[j] >= d[que[tail - 1]]))
    23                 --tail;
    24             que[tail++] = j;
    25         }
    26         printf("%d
    ", f[n]);
    27     }
    28     return 0;
    29 }
    P3

    4.乘车路线

    法一.搜索 + 剪枝

    首先随意用一种图论算法(Floyd-Warshall 也行, 毕竟 n 最大 100, 但我还是写了个 SPFA)得到起点到终点最小费用, 如果大于 k,

    说明钱带得实在太少啦, 跑不动! 如果钱足够的话,就开始搜索咯

    用 sid_w[i] 记录点 i 到终点 n 的最小费用

    用 sid_t[i] 记录点 i 到终点 n 的最短长度(原题是“长度”,可训练赛的时候被改成了“时间”)

    如果走最便宜的路钱还不够, 即 当前剩余费用 - 欲搜索道路费用 < 搜索目标点到终点最小费用 ,剪枝

    如果走最短的路还比已经得出的合理最短路程远,  即 当前已走长度 + 欲搜索道路长度 >= 合理最短长度, 剪枝

    另外还有一点就是搜索某个点时最好先标记, 搜索完之后再取消标记, 避免回路和环

      1 #include <cstdio>
      2 #include <algorithm>
      3 
      4 const int INF = 100000000;
      5 
      6 int n, k, m, ans = INF;
      7 int que[1000005], sid_w[1000005], sid_t[1000005];
      8 int fst[105], nxt[10005], u[10005], v[10005], w[10005], t[10005];
      9 int anti_fst[105], anti_nxt[10005];
     10 bool bok[1000005], flag[1000005];
     11 char tt[30];
     12 
     13 inline void getnum(int &num)
     14 {
     15     char tt;
     16     while ((tt = getchar()) < '0' || tt > '9');
     17     num = tt - '0';
     18     while ((tt = getchar()) >= '0' && tt <= '9')
     19         num = num * 10 + tt - '0';
     20     return ;
     21 }
     22 
     23 inline void putnum(int num)
     24 {
     25     int top = 0;
     26     do
     27         tt[++top] = num % 10;
     28     while (num /= 10);
     29     while (top)
     30         putchar(tt[top--] + '0');
     31     return ;
     32 }
     33 
     34 void anti_SPFA(int beg, int *dis, int *weight)
     35 {
     36     int head, tail, i, node;
     37     //init
     38     for (i = 1; i <= n; ++i) {
     39         dis[i] = INF;
     40         bok[i] = false;
     41     }
     42 
     43     dis[beg] = 0;
     44     head = tail = 1;
     45     que[tail++] = beg;
     46     bok[beg] = true;
     47     while (head < tail) {
     48         bok[node = que[head++]] = false;
     49         for (i = anti_fst[node]; i; i = anti_nxt[i]) {
     50             if (dis[node] + weight[i] >= dis[u[i]])
     51                 continue;
     52             dis[u[i]] = dis[node] + weight[i];
     53             if (bok[u[i]])
     54                 continue;
     55             que[tail++] = u[i];
     56             bok[u[i]] = true;
     57         }
     58     }
     59     return ;
     60 }
     61 
     62 void dfs(int node, int rem, int tim)
     63 {
     64     if (node == n) {
     65         ans = std:: min(ans, tim);
     66         return ;
     67     }
     68     int i;
     69     for (i = fst[node]; i; i = nxt[i])
     70         if (!flag[v[i]] && rem - w[i] >= sid_w[v[i]]
     71         && tim + t[i] + sid_t[v[i]] < ans) {
     72             flag[v[i]] = true;
     73             dfs(v[i], rem - w[i], tim + t[i]);
     74             flag[v[i]] = false;
     75         }
     76     return ;
     77 }
     78 
     79 int main()
     80 {
     81     
     82     int i, t1, t2, j;
     83     getnum(k);
     84     getnum(n);
     85     getnum(m);
     86     
     87     
     88     for (i = 1; i <= m; ++i) {
     89         getnum(u[i]);
     90         getnum(v[i]);
     91         getnum(t[i]);
     92         getnum(w[i]);
     93         nxt[i] = fst[u[i]];
     94         fst[u[i]] = i;
     95         anti_nxt[i] = anti_fst[v[i]];
     96         anti_fst[v[i]] = i;
     97         
     98     }
     99     
    100     anti_SPFA(n, sid_w, w);
    101     if (sid_w[1] > k) {
    102         printf("NO
    ");
    103         return 0;
    104     }
    105     
    106     anti_SPFA(n, sid_t, t);
    107     
    108     dfs(1, k, 0);
    109     putnum(ans);
    110 
    111     return 0;
    112 }
    P4_(1)

    法二.二维最短路

    dis[i][cost] 表示起点到 i 点在费用最大为 cost 的情况下的最短长度, 跑一个 SPFA 或者 Dijkstra 之后答案是 dis[n][k]

    我写的是 SPFA :

     1 #include <stdio.h>
     2 
     3 const int INF = 100000000;
     4 
     5 int fst[100005], nxt[100005], v[100005], l[100005], t[100005];
     6 int dis[10005][10005], que[100005];
     7 int n, k, w, u;
     8 bool bok[100005];
     9 
    10 void SPFA(int beg)
    11 {
    12     int i, j, head, tail, node;
    13     for (i = 1; i <= n; ++i) {
    14         bok[i] = false;
    15         for (j = 0; j <= w; ++j)
    16             dis[i][j] = INF;
    17     }
    18     for (j = 0; j <= w; ++j)
    19         dis[beg][j] = 0;
    20     head = tail = 1;
    21     que[tail++] = beg;
    22     bok[beg] = true;
    23     while (head < tail) {
    24         bok[node = que[head++]] = false;
    25         for (i = fst[node]; i; i = nxt[i])
    26             for (j = t[i]; j <= w; ++j) {
    27                 if (dis[node][j - t[i]] + l[i] >= dis[v[i]][j])
    28                     continue;
    29                 dis[v[i]][j] = dis[node][j - t[i]] + l[i];
    30                 if (bok[v[i]])
    31                     continue;
    32                 que[tail++] = v[i];
    33                 bok[v[i]] = true;
    34             }
    35     }
    36     return ;
    37 }
    38 
    39 int main()
    40 {
    41     int i;
    42     scanf("%d%d%d", &w, &n, &k);
    43     for (i = 1; i <= k; ++i) {
    44         scanf("%d%d%d%d", &u, &v[i], &l[i], &t[i]);
    45         nxt[i] = fst[u];
    46         fst[u] = i;
    47     }
    48     SPFA(1);
    49     if (dis[n][w] != INF)
    50         printf("%d
    ", dis[n][w]);
    51     else
    52         printf("NO
    ");
    53     return 0;
    54 }
    P4_(2)

    改悔:

    1题这类找通项公式题可以暴力算些结果再观察规律嘛, 毕竟对于数学渣来说推式子挺耗时间的

    搜索剪枝一定要剪干净啊!剪一半真的只有 50 分, 不标记已访问点导致程序原地转圈也是!

    说到二维最短路, 我已经不会写次短路的代码了!

    单调队列一年前就看过课件,结果忘光了, DP + 单调队列 很常见嘛

    题目:

    P3496数三角形
    时间限制 : 10000 MS   空间限制 : 65536 KB
    评测说明 : 时限1000ms
    问题描述

    给出一个正整数n,从1,2,3.....n 中选出三个不同整数,使得以它们为三边长可以组成三角形,问
    总共有多少种不同的三角形?
    例如,n=5 时有三种:(2,3,4) , (2,4,5) , (3,4,5)

    输入格式

    一个正整数n

    输出格式

    一个整数,表示三角形的个数

    样例输入 1

    5

    样例输出 1

    3

    样例输入 2

    30

    样例输出 2

    1925

    样例输入 3

    8

    样例输出 3

    22

    提示

    【数据范围】
    对于30%的数据,3<=n<=100
    对于100%的数据,3<=n<=1,000,000

    P4236翻硬币
    时间限制 : - MS   空间限制 : 165536 KB 
    评测说明 : 1s
    问题描述

    两个玩家在玩一个有趣的翻硬币游戏。

    有 N 枚硬币排成一排,有的正面朝上,有的反面朝上。从左往右硬币按1 到N 编号。玩家轮流操作。每次操作,玩家选一枚正面朝上的硬币,将它翻转,同时在该硬币左侧连续四个硬币中,再任选一个硬币,将其翻转。
        具体而言,假设第i号硬币正面朝上。若将第i号硬币翻转后,必须在编号为i-1,i-2,i-3,i-4的四个硬币中选一个进行翻转。若i<=4,则可只翻转i号硬币,也可以再在1到i-1之间选一个进行翻转。

    谁没有硬币可翻谁就算输。两个玩家都非常聪明,问先手是否获胜?

    输入格式

    第一行,一个正整数T,表示接下来有T组测试数据。对于每组测试数据:

    第1行,一个整数N,表示硬币的数量。
    第2行,N个空格间隔的整数(0和1),从左往右依次表示游戏开始前硬币的情况,其中数字0表示正面朝下,数字1表示正面朝上。

    输出格式

    T行,每行对应一组测试数据的答案。若先手胜输出”Yes” 否则输出“No”

    样例输入

    5
    9
    1 0 1 1 1 0 1 0 0 
    13
    0 0 1 1 1 1 0 1 1 1 0 0 1 
    12
    1 0 1 0 1 0 1 1 1 0 0 0 
    9
    0 0 0 0 1 0 0 0 0 
    13
    1 0 1 0 1 1 0 0 0 0 0 0 1 

    样例输出

    Yes
    Yes
    Yes
    No
    No

    提示

    对于30%的数据:  1≤N≤100

    对于100%的数据: 1≤N≤100000 ,T≤10

    P3774小鸟
    时间限制 : - MS   空间限制 : 65536 KB 
    评测说明 : 1000ms
    问题描述

    有一排n棵树,第i棵树的高度是Di。
    一群小鸟要从第1棵树飞到第n棵树去玩。
    不同小鸟的飞跃能力不同,第i只小鸟的飞跃能力为ki,表示如果当前它位于第x号树,那么它可以飞到x+1,x+2,......,x+ki号树上去,也就是一次可以飞过ki棵树。
    如果小鸟飞到一棵不矮于当前树的树,那么他的劳累值会+1,否则不会。
    小鸟们希望最小化劳累值,请你计算每只小鸟达到终点所需最小劳累值。

    输入格式

    第一行,一个整数N(2<=N<=100000)
    第二行,N个空格间隔的整数,第i个数表示第i棵树的高度Di。(1<=Di<=10^9)
    第三行,一个整数Q(1<=Q<=25),表示小鸟的数量
    接下来Q行,每行一个整数,其中第i个整数表示第i只小鸟的飞跃能力ki。

    输出格式

    Q行,每行一个整数,表示对应小鸟的劳累值

    样例输入

    9
    4 6 3 6 3 7 2 6 5
    2
    2
    5

    样例输出

    2
    1

    提示

    第1只小鸟停落的树编号为1, 3, 5, 7, 8, 9.在3飞到5时劳累值+1,在7飞到8时劳累值+1 


    来源  POI 2014 Little Bird
    P2407乘车路线
    时间限制 : 10000 MS   空间限制 : 165536 KB
    问题描述

    编号为 1.. N 的N座城镇用若干仅供单向行驶的道路相连,每条道路上均有两个参数:道路长度(length)和在该条道路上行驶的费用(cost)。
    BOB 准备从城镇 1 出发到达城镇 N,但他目前只有 W 块钱,为此,你需要帮助他寻找一条从城镇1到城镇 N 在他能支付的前提下的一条最短路线。

    输入格式

    第一行为钱的数目W (0<=w<=1000)
    第二行为城镇数目N(2<=N<=100)
    第三行为为道路条数K(1<=K<=10000)
    随后的 K 行每行为一条道路的信息,包含 4个数值(S,D,L,T)其中S为道路的起点, D为道路的终点 , L为道路长度, T为所需支付 费用。
    (1<=S,D<=N,1<=L<=100,0<=T<=100)

    输出格式

    输出最短长度,若无解,则输出“NO”;

    样例输入




    1 2 2 3 
    2 4 3 3 
    3 4 2 4 
    1 3 4 1 
    4 6 2 1 
    3 5 2 0 
    5 4 3 2 

    样例输出

    11


    来源  CEOI1998
  • 相关阅读:
    待解决的问题
    Java编译环境的搭建(eclipse)
    Ubuntu升级没有声音的解决方法
    Ubuntu中文输入法的添加
    apue和error
    韦东山linux学习之ubuntu 9.10 软件源 问题
    查询表的列名
    事务管理
    数组 String[]
    java 内存
  • 原文地址:https://www.cnblogs.com/ghcred/p/7416237.html
Copyright © 2011-2022 走看看