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\n%.10lf %.10lf\n", 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\n", dp[len1][len2]);

                   path(len1, len2); printf("\n");

         }

}

 

                  

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

posted @ 2020-01-13 01:34  loenvom  阅读(150)  评论(0编辑  收藏  举报