2020icpc上海站(正式赛) 部分题解

目录

 

B. Mine Sweeper II

大致题意:

思路:

代码:

D. Walker

大致题意:

思路:

注意:

第一种:

第二种:

第三种:

代码:

I. Sky Garden

大致题意:

思路:

代码:

G. Fibonacci(正宗签到题)

大致题意:

思路:

代码:

M. Gitignore

大致题意:

思路:

代码:

C:Sum of log   https://blog.csdn.net/m0_50623076/article/details/111565108


B. Mine Sweeper II

大致题意:

给你一个n×m的矩阵,X代表雷,.代表没有雷。扫雷的游戏。为X的点权值为0,为.的点权值为相邻八个的是雷(X)的数目(当然,如果如果在该矩阵边缘的话不是八个)。

现在给你两个图A和图B,求得再不超过nm/2次数修改B(.->X 或则X->.)使得sum与A得相同。

思路:

求B得sum和A的相同,如果只是这样记录,bfs等等进行求解的话,很难做到。因为你修改一个影响到了八个,并且不仅仅是X->. 还有.->X。既然可以修改近一半的点,是不是可以考虑下直接让B为A的方法?(样例不是这样,引导你不这样想,巨坑!)。如果字符不同的个数小于等于nm/2的话就可以。X和.是相互作用的(啥意思呢?就是说如果.得1证明周围有X,那么这个X周围也有一个.)。换句话说,如果一个图的X全部变成了.,而.全部变成X,这样的话sum不变。如果B变到A需要大于nm/2的话,变到A经过上述转换的就小于nm/2了(因为之前对A变的对经过转化后的A不再变化)。

 

代码:

#include<algorithm>

#include<iostream>



using namespace std;

const int maxn = 1e3+50;

char a[maxn][maxn] ,b[maxn][maxn];

int main(){

    int n,m;

    cin >> n >> m;

    for(int i = 0;i < n;i++)  scanf("%s",a+i);

    for(int i = 0;i < n;i++)  scanf("%s",b+i);

    int cnt = 0;

    for(int i = 0;i < n;i++)

       for(int j = 0;j < m;j++)

           if(a[i][j] != b[i][j])   cnt++;

    if(cnt > n*m/2){

       for(int i = 0;i < n;i++)

           for(int j = 0;j < m;j++)

              if(a[i][j] == '.') a[i][j] = 'X';

              else a[i][j] = '.';

    }

    for(int i = 0;i < n;i++)

       printf("%s\n",a+i);

    return 0;

}

D. Walker

大致题意:

在一个长为n的路上,在d1,d2两个位置有两个人速度为v1,v2。问最少需要多长时间能够至少在每个位置都有人走过(一个人走过即可)。

思路:

(单调联想二分! 最值联想二分!)

注意:

  1. 先把postion小的放到d1!!!
  2. 虽然精确1e-6,但是eps要为1e-7才管得到1e-6。

首先各走各的互不影响并且可能存在到目的地不走了等另一个人的情况

第一种:

一个人全部走过。

ans=min((min(d1,n-d1)+n)/v1,(min(d2,n-d2)+n)/v2)

即对每一个人,选择距离左端或右端较近的,然后折返走全程。

第二种:

每个人经过另一个人到端点

ans=max((n-d1)/v1,d2/v2)

第三种:

顾自己这面的,先把自己的尽量走完。

二分时间,看看规定时间内是否可以走完即可。

 

右端点显然是目前所求的最小ans。左端点一定注意,应该起码能够让每个人到相应方向端点,如果一个人到不了的话,另一个人又想走完,其实就是一个人走完全程的情况。

 

对于左边的人最远能到距离左端点右边多少,要么先向左(重复d1),要么先向右(重复d1右边)。即:

pos1 = max(mid*v1-2*d1,(mid*v1-d1)/2)+d1;

对于右边的人最远能到距离右端点左边多少,要么先向左(重复d2左边),要么先向右(重复n-d2)。即:

pos2 = max(mid*v2-2*(n-d2),(mid*v2-(n-d2))/2)+(n-d2);

如果pos1 >= n-pos2证明这两个人相遇了,也就是说可以走完。

 

代码:

#include<iostream>

#include<algorithm>

#include<cmath>

using namespace std;

const double esp = 1e-9;

double n,v1,v2,d1,d2;

double ans;

double check(double mid){

    double pos1 = max(mid*v1-2*d1,(mid*v1-d1)/2)+d1;

    double pos2 = max(mid*v2-2*(n-d2),(mid*v2-(n-d2))/2)+(n-d2);

    pos2 = n-pos2;

    if(pos2 <= pos1) return true;

    return false;

}

double fmax(double x,double y){

    if(x > y)    return x;

    else return y;

}

double fmin(double x,double y){

    if(x > y)    return y;

    else return x;

}

int main(){

    int t;

    cin >> t;

    while(t--){

        cin >> n >> d1 >> v1 >> d2 >> v2;    

        if(d1 > d2){

            double t = d1;

            d1 = d2;

            d2 = t;

            t = v1;

            v1 = v2;

            v2 = t;        

        }

        // first:

        double x1 = fmin(n-d1,d1);

        double x2 = fmin(n-d2,d2);

        x1+=n,x2+=n;

        ans = fmin(x1/v1,x2/v2);

        //second:

        ans = fmin(ans,fmax((n-d1)/v1,d2/v2));

        //third:

        double l = max(d1/v1,(n-d2)/v2),r = ans;

        while(fabs(r-l) > esp){

            double mid = (l+r)/2;

            if(check(mid)){

                r = mid;           

                ans = min(ans,mid);

            }

            else l = mid;

             

        }

        printf("%.10lf\n",ans);

    }  

    return 0;

}

I. Sky Garden

大致题意:

有n个同心圆(第i个半径为i),有m条线平分圆。问所有交点之间的最短distance和。

思路:

    首先计算center也就是中心点到其他所有点的距离。

注意 当m=1时,不可以加center 因为没有该点。m条直线,2m条从中心射出。每一条的距离为1+2+3+…+n=(n+1)n/2,2m条,所以center=nm(n+1);

如果只要一个点到该层所有点的距离,称m后就是该层到该层的sum

(只要弧度<=2时才走弧线,只有弧度>2时该层才会出现走内层再回来比直接在该层近,所以同层内到同层内其他点与内外环无关)

外层到内层必须经过1到下层,也就是该点到内层某点的距离等于同直线相交的内层点到该层的距离+1;

建立数组point代表该点到同层所有点的最小距离和

建立数组cir代表该点到该点所在圈以及以内所有点(除中心点外)的所有距离和。

于是得每一曾

        for(int j = 1;j < m;j++){

            double tmp = j*1.0*theta;

            point[i] += f_min(2.0*i,tmp*i);

        }

point[i]*=2.0;point[i]+=(2.0*i);

cir[i] = cir[i-1]+(i-1)*2.0*m/*该点到内曾某点得距离为内一层到对应点得距离+1,因为有(i-1)*2*m个点*/+point[i];

 

代码:

#include<algorithm>

#include<iostream>

#include<cmath>

using namespace std;

const double pi = acos(-1);

const int maxn = 1000;

double point[maxn];

double cir[maxn];

double ans = 0;

double f_min(double a,double b){

    return a<b?a:b;

}

int main(){

    double n,m;

    cin >> n >> m;

    double center = n*(n+1)*m;

    double theta = pi/(1.0*m);

    for(int i = 1;i <= n;i++){

       for(int j = 1;j < m;j++){

           double tmp = j*1.0*theta;

           point[i] += f_min(2.0*i,tmp*i);

       }

       point[i]*=2.0;point[i]+=(2.0*i);

       ans += point[i]*1.0*m;

       ans += (cir[i-1]+(i-1)*2.0*m)*2.0*m;

       cir[i] = cir[i-1]+(i-1)*2.0*m+point[i];

    }

    if(m > 1)

       ans += center;

    printf("%.10lf\n",ans);

    return 0;

}

G. Fibonacci(正宗签到题)

大致题意:

f代表斐波那契数列,问i=1:n j=i+1:n中 fi*fj为偶数得个数。

思路:

斐波那契,前两项和得性质。

奇 奇 偶 奇 奇 偶 奇 奇 偶 奇 奇 偶 奇…

对于奇数与偶数相乘可

t=m/3;---t个偶数

为2*(t+t-1+…+1)=t(t+1);

对于偶数与所有奇数偶数都可以

为3*(t-1+t-2+….+1)=3t(t-1)/2;

然后对于m%3只有偶数可以,也就是再称t即可。

 

代码:

#include<algorithm>

#include<iostream>



using namespace std;

typedef long long ll;

int main(){

    ll n;

    cin >> n;

    ll t = n/3;

    ll mod = n%3;

    ll ans = t*(t+1);

    ans += t*mod;

    ans += 3*t*(t-1)/2;

    cout << ans << endl;

    return 0;

}

M. Gitignore

大致题意:

想给你n个文件的路径,你需要删除的,再给你m个文件的路径,你不能删除的,如果一个文件夹中的所有文件都可以删除那么就一次性删除即可,问最少删除多少次。

思路:

先对n个路径建树,再对m个路径建树,后者建树时,标记所有经过的路径意味着不可以直接删除,最后从根遍历遇到第一个未标记返回1即可。

 

用map超时,用指针ak。看到大佬的代码,自叹不如。

所有文件map为0,所有m的路径map为1。先用个rec记录n(总共需要删除的文件数量)。遍历n个需要删除的路径。如果遇到了1 continue(因为不可能删除)。如果遇到了0标记为2(意味着这和文件夹里没有不能删除的且以及遍历到了一个文件在里面)。如果遇到了2,就rec--  break!!!;(因为这里面以及有一个了,如果还有一个,就可以不能删除它,直接删除该文件夹即可 )。其实就是看在一个可以直接删除的文件夹中有多少个文件,遇到一个记录,遇到第二个--,表明可以少进行一次删除操作。注意break,因为在这里直接删除该文件夹,子文件夹以及没有用处了。

 

代码:

#include<iostream>

#include<map>

#include<string>

#include<algorithm>

using namespace std;



int main(){

    int t;

    cin >> t;

    while(t--){

       string s[105];

       int n,m;

       cin >> n >> m;

       int ans = n;

       map<string,int>mp;

       for(int i = 1;i <= n+m;i++){

           cin >> s[i];

           string name;name.clear();

           for(int j = 0;j < s[i].size();j++){

              if(s[i][j] != '/')    name+=s[i][j];

              else {

                  if(i > n)

                     mp[name] = 1;

                  else mp[name] = 0;

              }

           }

       }

       for(int i = 1;i <= n;i++){

           string name;name.clear();

           for(int j = 0;j < s[i].size();j++){

              if(s[i][j] != '/')    name+=s[i][j];

              else {

                  if(mp[name] == 0) mp[name] = 2;

                  else if(mp[name] == 1) continue;

                  else if(mp[name] == 2){

                     ans--;

                     break;

                  }

              }

           }         

       }

       cout << ans << endl;

    }

    return 0;

}

 

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值