zoukankan      html  css  js  c++  java
  • 小测(noip2005的两道题) 2017.3.3

    过河

    题目描述 Description

    在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
    题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

    输入描述 Input Description

    输入第一行有一个正整数L(1<=L<=109),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1<=S<=T<=10,1<=M<=100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。

    输出描述 Output Description

    输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。

    样例输入 Sample Input

    10
    2 3 5
    2 3 5 6 7

    样例输出 Sample Output

    2

    数据范围及提示 Data Size & Hint

    对于30%的数据,L<=10000;
    对于全部的数据,L<=109


      考虑优化一下暴力 dp 的部分。

    定理 如果正整数 $a, b$ 满足 $a > 1$ 或者 $b > 1$,且 $(a, b) = 1$,那么 $ax + by (x geqslant 0, y geqslant 0)$ 不能表示出的最小自然数是 $ab - a - b$

      证明 首先考虑证明不存在 $x, y$ 使得 $ax + by = ab - a - b$。假设存在整数 $x, y$。

      那么有 $a(x + 1) + b(y + 1) = ab$。

      所以 $a mid b(a + y + 1)$,所以 $a mid (y + 1)$。同理可得 $b | (x + 1)$。

      因为 $x geqslant 0, y geqslant 0, ab > 0$,所以 $a(x + 1) + b(y + 1) geqslant ab + ab = 2ab > ab$,矛盾。

      然后证明对于任意自然数 $c > ab - a - b$ 都能表示出来。根据裴蜀定理有,存在整数$x_0, y_0$,使得 $a(x_0 + kb) + b(y_0 - ka) = d$,如果 $x_0 geqslant 0, y_0 geqslant 0$ 那么找到了一组合法的解,如果 $x_0 <0, y_0 < 0$ 显然不可能,否则 $x_0 < 0$ 或 $y_0 < 0$,不妨设 $x_0 geqslant 0, y_0 < 0$,如果 $x_0 geqslant b$,那么令 $x_0' = x_0 - b, y_0' = y_b + a$ ,所以只用考虑当 $0 leqslant x_0 < b, y_0 < 0$ 的情况,此时有 $a cdot x_0 + bcdot y_0 leqslant ab - a - b$,这和 $a cdot x_0 + bcdot y_0 = c > ab - a - b$ 矛盾。

      如果 $T > S$,那么至少包含 $x$ 和 $x + 1$,因为 $(x, x + 1) = 1$,所以对于长度大于 $x(x + 1) - x - x - 1 < 100$ 的长度可以直接看成 100,因为它们总能走到。

      如果 $T = S$,可以简单处理掉。

    Code

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cctype>
     4 #include<cstring>
     5 #include<cstdlib>
     6 #include<fstream>
     7 #include<sstream>
     8 #include<algorithm>
     9 #include<map>
    10 #include<set>
    11 #include<queue>
    12 #include<vector>
    13 #include<stack>
    14 using namespace std;
    15 typedef bool boolean;
    16 #define INF 0xfffffff
    17 #define smin(a, b) a = min(a, b)
    18 #define smax(a, b) a = max(a, b)
    19 
    20 ifstream fin("river.in");
    21 ofstream fout("river.out");
    22 
    23 int M, L;
    24 int l, r;
    25 int* d;
    26 int* s;
    27 int* f;
    28 
    29 inline void init() {
    30     fin >> L;
    31     fin >> l >> r >> M;
    32     d = new int[(const int)(M + 1)];
    33     for(int i = 1; i <= M; i++)
    34         fin >> d[i];
    35     sort(d + 1, d + M + 1);
    36 }
    37 
    38 inline void solve() {
    39     if(l == r) {
    40         int res = 0;
    41         for(int i = 1; i <= M; i++) {
    42             if(d[i] % l == 0)
    43                 res++;
    44         }
    45         fout << res;
    46         return;
    47     }
    48     s = new int[(const int)(M * 100 + 5)];
    49     memset(s, 0, sizeof(int) * (M * 100 + 5));
    50     d[0] = 0;
    51     int newd = 0;
    52     for(int i = 1; i <= M; i++) {
    53         if(d[i] - d[i - 1] > 100)
    54             newd += 100;
    55         else newd += d[i] - d[i - 1];
    56         s[newd]++;
    57     }
    58     f = new int[(const int)(newd + 1 + r)];
    59     memset(f, 0x3f, sizeof(int) * (newd + 1 + r));
    60     f[0] = 0;
    61     for(int i = 0; i <= newd; i++) {
    62         if(f[i] > M)    continue;
    63         for(int j = l; j <= r; j++)
    64             smin(f[i + j], f[i] + s[i + j]);
    65     }
    66     int res = M;
    67     for(int i = newd; i <= newd + r; i++)
    68         if(f[i] >= 0)
    69             smin(res, f[i]);
    70     fout << res;
    71 }
    72 
    73 int main() {
    74     init();
    75     solve();
    76     return 0;
    77 }

    题目描述 Description

      佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有n个同学,编号从1到n。一开始,同学们按照1,2,……,n的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。
    佳佳可向同学们下达命令,每一个命令的形式如下:
    (b1,b2,...bm-1,bm)
      这里m的值是由佳佳决定的,每次命令m的值都可以不同。这个命令的作用是移动编号是b1,b2,…… bm –1,bm的这m个同学的位置。要求b1换到b2的位置上,b2换到b3的位置上,……,要求bm换到b1的位置上。
      执行每个命令都需要一些代价。我们假定如果一个命令要移动m个人的位置,那么这个命令的代价就是m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?

    输入描述 Input Description

    输入第一行是一个整数n(3<=n<=50000),表示一共有n个同学。其后n行每行包括两个不同的正整数,以一个空格隔开,分别表示编号是1的同学最希望相邻的两个同学的编号,编号是2的同学最希望相邻的两个同学的编号,……,编号是n的同学最希望相邻的两个同学的编号。

    输出描述 Output Description

    这一行只包含一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出-1。

    样例输入 Sample Input

    4
    3 4
    4 3
    1 2
    1 2

    样例输出 Sample Output

    2

    数据范围及提示 Data Size & Hint

    【数据规模】
    对于30%的数据,n<=1000;
    对于全部的数据,n<=50000。


      因为这是一个环,所以可以剖环成链,因为剖开点不一样,就会导致结果不一样,所以正着反着每个地方都去剖一次。

      接下来是考虑如何快速地求解需要付出的代价。这个代价等于所有在它不该在的位置的数量。

      这个结论可以这么来说明,对于一个在它不该在的位置的i,那么必定存在它占掉另一个的位置且另一个占掉了它的位置,当按照这个关系连接起来就等于一个环,刚好可以按照题目中的变换方式(旋转这个环),把环上的数变到它应该在的位置。

      但是这么做仍然会超时,所以得继续考虑优化。对于改变剖开点的位置,我们规定每次改变把剖开点向前挪动一次,那么我们可以记录每个位置上的数,离它第一次回到自己正确位置上需要挪动剖开点多少次,然后就找个最大值,然后用n一减就可以出结果。

      注意判断是否可以满足所有需求。主要的原因是:a希望坐在b,c旁边,但是b,c中有人不是最希望坐在a的旁边。

    Code

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cctype>
      4 #include<cstring>
      5 #include<cstdlib>
      6 #include<fstream>
      7 #include<sstream>
      8 #include<algorithm>
      9 #include<map>
     10 #include<set>
     11 #include<queue>
     12 #include<vector>
     13 #include<stack>
     14 using namespace std;
     15 typedef bool boolean;
     16 #define INF 0xfffffff
     17 #define smin(a, b) a = min(a, b)
     18 #define smax(a, b) a = max(a, b)
     19 
     20 template<typename T>class Matrix{
     21     public:
     22         T *p;
     23         int lines;
     24         int rows;
     25         Matrix():p(NULL){    }
     26         Matrix(int rows, int lines):lines(lines), rows(rows){
     27             p = new T[(lines * rows)];
     28         }
     29         T* operator [](int pos){
     30             return (p + pos * lines);
     31         }
     32 };
     33 #define matset(m, i, s) memset((m).p, (i), (s) * (m).lines * (m).rows)
     34 
     35 ifstream fin("fire.in");
     36 ofstream fout("fire.out");
     37 
     38 int n;
     39 Matrix<int> sit;
     40 int* exist;
     41 
     42 inline void init() {
     43     fin >> n;
     44     sit = Matrix<int>(n + 1, 2);
     45     exist = new int[(const int)(n + 1)];
     46     memset(exist, 0, sizeof(int) * (n + 1));
     47     for(int i = 1; i <= n; i++) {
     48         fin >> sit[i][0] >> sit[i][1];
     49         for(int j = 0; j < 2; j++) {
     50             exist[sit[i][j]]++;
     51             if(exist[sit[i][j]] > 2 || sit[i][j] == i) {
     52                 fout << "-1";
     53                 exit(0);
     54             }
     55         }
     56     }
     57     for(int i = 1, c; i <= n; i++) {
     58         for(int j = 0; j < 2; j++) {
     59             c = sit[i][j];
     60             if(sit[c][0] != i && sit[c][1] != i) {
     61                 fout << "-1";
     62                 exit(0);
     63             }
     64         }
     65     }
     66 }
     67 
     68 int myabs(int x) {    return (x < 0) ? (x + n) : (x);    }
     69 
     70 int* list;
     71 int* round;
     72 
     73 int calc(int x) {
     74     list[1] = 1;
     75     list[2] = sit[1][x];
     76     int last = list[2];
     77     memset(round, 0, sizeof(int) * (n + 1));
     78     round[0]++, round[myabs(2 - last)]++;
     79     int ret = max(round[0], round[myabs(2 - last)]);
     80     for(int i = 3, j; i <= n; i++) {
     81         if(sit[last][0] == list[i - 2])
     82             list[i] = sit[last][1];
     83         else list[i] = sit[last][0];
     84         last = list[i];
     85         j = myabs(i - last);
     86         round[j]++;
     87         smax(ret, round[j]);
     88     }
     89     return ret;
     90 }
     91 
     92 int res = INF;
     93 inline void solve() {
     94     list = new int[(const int)(n + 1)];
     95     round = new int[(const int)(n + 1)];
     96     for(int i = 0, a; i < 2; i++) {
     97         a = calc(i);
     98         smin(res, n - a);
     99     }
    100     fout << res;
    101 }
    102 
    103 int main() {
    104     init();
    105     solve();
    106     return 0;
    107 }
  • 相关阅读:
    有关C#中的引用类型的内存问题
    C# Data Parse
    测绘仪器的精度
    Tomcat部署时war和war exploded的区别
    「小程序JAVA实战」小程序模板在外部页面引用(20)
    「小程序JAVA实战」小程序模块之间引用(19)
    「小程序JAVA实战」小程序模块页面引用(18)
    「小程序JAVA实战」小程序通用模板的使用(17)
    「小程序JAVA实战」小程序视图之细说wx:key列表高级特性(16)
    「小程序JAVA实战」小程序视图之条件判断(15)
  • 原文地址:https://www.cnblogs.com/yyf0309/p/6498427.html
Copyright © 2011-2022 走看看