• 2020_1_12


    夜深人静,正是思考问题的好时间。

    整理一下做过的题。

    先是最近做的个人赛【2013南京现场赛】。

    A

             X Y 初始值都是1

             然后每次增加X 会让Y增加Y/X (保留整数) 或者只增加Y

    大的思路是一个贪心,可以根据条件推算出Y最终的表达式,那么我们可以贪心的想一定是在最前面去放Y是最优的。

    记录floor 和 ceil

    B

             求就C(N,i)排列组合,抑或结果的求和

             看到抑或,那么就需要思考二进制。

             对于每次组合的求和结果,只有可能当相应的二进制位上的数字为奇数时,才可以产生贡献值。

             所以我们对数字分解,然后对于每一个i都计算一次排列组合情况就可以了。

             由于N只有1000所以O(n^2) 水过

    C

             再一次看错了题目,真就是觉得眼睛没用。

             给出三种颜色的球的个数。

             每次放球会有一个得分,放两侧那么得分是球的不同颜色数。

             放球间,那么得分是放置位置前侧的不同颜色数+放置位置后侧的不同颜色数

            

             然后暴力讨论就可以。

    今晚的Atcoder

    D

             给出一个序列,然后问K个组合的(max-min)的和是多少。

             数据量是100000

             很容易就会想到可能的差值是哪些,也很好求,但是问题在与排列组合的方法。直接暴力,打表存不下,时间复杂度也会爆炸。

             可以思考到,对于跨度为d的区间来说,可以产生的贡献值就是(max-min)* C(d-2, k-2)

             C(d-2, k-2) 可以推算由上一次的值推算出来,需要乘法逆元。

             (max - min) 可以用两次前缀和求出来。

    E

             是一个点的最小圆覆盖问题,一直以为直接模拟退火没毛病,但是今天被WA,只能老老实实的学几何的做法,再一看数据100000,果然模拟退火不是一个好办法。

             顺便记录一下dalao代码里的好用的几何套路。

    #include <stdio.h>
    
    #include <math.h>
    
    #include <algorithm>
    
    using namespace std;
    
    double sqr(double x)
    
    {
    
             return x * x;
    
    }
    
    class point
    
    {
    
             public:
    
                       double x, y;
    
                       point(double x, double y) : x(x), y(y){}
    
                       point(){}
    
             //      point(point &t) : x(t.x), y(t.y){}
    
                       double len()
    
                       {
    
                                return sqrt(sqr(x) + sqr(y));
    
                       }                
    
                       point rotate()
    
                       {
    
                                return point(y, -x);
    
                       }
    
                       point operator - (point t)
    
                       {
    
                                return point(x-t.x, y-t.y);
    
                       }
    
                       point operator + (point t)
    
                       {
    
                                return point(x+t.x, y+t.y);
    
                       }
    
                       point operator / (int t)
    
                       {
    
                                return point(x/t, y/t);
    
                       }
    
                       point operator * (double t)
    
                       {
    
                                return point(x * t, y * t);
    
                       }
    
                      
    
    }a[100009], cir;
    
    point line_p(point p0, point v, point p1, point w)
    
    {
    
             double t = cross(p1 - p0, w) / cross(v, w);
    
             return p0 + v * t;
    
    }
    
    /*
    
    记录一下自己艰险的手撸推算过程
    
    谁能想到这个公式是化简的结果,谁能想到!
    
    (p0 + v * t - p1) * w = 0;
    
    (p0.x + v.x * t - p1.x, p0.y + v.y * t - p1.y) * (w.x, w.y) = 0;
    
    (p0.x + v.x * t - p1.x) * w.y = (p0.y + v.y * t - p1.y) * w.x
    
    p0.x * w.y + v.x * t * w.y - p1.x * w.y = p0.y * w.x + v.y * t * w.x - p1.y * w.x
    
    (v.x * w.y - v.y * w.x) * t = p0.y * w.x - p0.x * w.y + p1.x * w.y - p1.y * w.x
    
    (v.x * w.y - v.y * w.x) * t = (p0.y - p1.y) * w.x - (p0.x - p1.x) * w.y
    
    t = cross(p1 - p0, w) / cross(v, w);
    
    */
    
    point circle(point a, point b, point c)
    
    {
    
             return line_p((a+b)/2, (b-a).rotate(), (a+c)/2, (c-a).rotate());
    
    }
    
    int main()
    
    {
    
             int n;
    
             scanf("%d", &n);
    
             for(int i = 1; i <= n; i++)
    
                       scanf("%lf %lf", &a[i].x, &a[i].y);
    
             random_shuffle(a+1, a+1+n);
    
             double r = 0;
    
             for(int i = 1; i <= n; i++)
    
             {
    
                       if((a[i] - cir).len() > r)
    
                       {
    
                                cir = a[i]; r = 0;
    
                                for(int j = 1; j < i; j++)
    
                                {
    
                                         if((a[j] - cir).len() > r)
    
                                         {
    
                                                   cir = (a[i] + a[j]) / 2;
    
                                                   r = (cir - a[i]).len();
    
                                                   for(int k = 1; k < j; k++)
    
                                                   {
    
                                                            if((cir-a[k]).len() > r)
    
                                                            {
    
                                                                     cir = circle(a[i], a[j], a[k]);
    
                                                                     r = (cir - a[i]).len();
    
                                                            }
    
                                                           
    
                                                   }
    
                                         }
    
                                }
    
                       }
    
             }
    
            
    
             printf("%.10lf
    %.10lf %.10lf
    ", r, cir.x, cir.y);
    
    }

    零散的题目

             CF1272F  

            

             给出两个括号串,问最短的括号串使得给出的字符串都是他的子串,不需要连续。

             Dalao的做法是dp[i][j][k] 真是想不到。

             以下为dalao的思路

             对于这个问题,我们可以分解为两个问题

             1、最小的字符串使得给出的字符串被包含

             2、这个字符串需要是合法的括号

             到此豁然开朗,但是代码还是有点问题。第二个子问题的方程还是很好写的。

    不是很明白第一个子问题的求解方法。

             于是,引申出了一个问题。

             对于两个只含小写字母的串,如何求出一个最短串来包含这两个串。

             比如 abb 和 bbc 那么答案应该是abbc

             在研究的了dalao的代码后,才得到了转移方程,然后记忆化路径就好了

    #include <stdio.h>
    
    #include <cstring>
    
    #include <algorithm>
    
    using namespace std;
    
    int dp[1009][1009];
    
    char s1[10009], s2[10009];
    
    struct node
    
    {
    
             int x, y;
    
             char add;
    
             node(){}
    
             node(int x, int y, char add) : x(x), y(y), add(add){}
    
    }pre[1009][1009];
    
    void path(int x, int y)
    
    {
    
             if(x==0 && y==0) return;
    
             path(pre[x][y].x, pre[x][y].y);
    
             putchar(pre[x][y].add);
    
    }
    
    int main()
    
    {
    
             int len1, len2;
    
             while(scanf("%s %s", s1+1, s2+1) != EOF)
    
             {
    
                       len1 = strlen(s1+1);
    
                       len2 = strlen(s2+1);
    
                       for(int i = 0; i <= len1; i++)
    
                                for(int j = 0; j <= len2; j++)
    
                                         dp[i][j] = 2e9;
    
                       dp[0][0] = 0;
    
                       for(int i = 0; i <= len1; i++)
    
                                for(int j = 0; j <= len2; j++)
    
                                         for(char k = 'a'; k <= 'z'; k++)
    
                                         {
    
                                                   int f1 = 0, f2 = 0;
    
                                                   if(s1[i+1]==k) f1=1;
    
                                                   if(s2[j+1]==k) f2=1;
    
                                                   dp[i+f1][j+f2] = min(dp[i+f1][j+f2], dp[i][j]+1);
    
                                                   if(dp[i+f1][j+f2]==dp[i][j]+1)
    
                                                   {
    
                                                            pre[i+f1][j+f2] = node(i, j, k);
    
                                                   }
    
                                         }
    
                       printf("%d
    ", dp[len1][len2]);
    
                       path(len1, len2); printf("
    ");
    
             }
    
    }

                      

             至此,这个问题才算完全搞明白了。

  • 相关阅读:
    c#中的构造方法
    c# Dictionary拓展2个key得到1个value
    虚拟主机的提权两个小技巧
    teamviewer提权
    域渗透:mstsc连接记录清理
    linux之 vim 常用命令
    Linux之 find 命令学习
    域渗透:MS14-068
    学习:脱壳之Anti Dump和修复PE
    学习:KiUserExceptionDispatcher之寻找OEP
  • 原文地址:https://www.cnblogs.com/loenvom/p/12185483.html
Copyright © 2020-2023  润新知