2020牛客NOIP赛前集训营-普及组(第六场)全部解析

本文介绍了四道编程竞赛题目,涉及数列求和、平面直角坐标系距离计算、二维板子动态规划及无向图最短路径问题。通过暴力求解、枚举优化、深度优先搜索及最短路径优先算法展示了不同的解题思路,旨在探讨竞赛中的高效解题方法。
摘要由CSDN通过智能技术生成

目录

  1. A 七七七七(水题)
  2. B 平面旅行(枚举)
  3. C 小球下落(dfs暴力)
  4. D 自由世界(spfa打脸正解)

A 七七七七

题目

链接
来源:牛客网

牛牛最近对7很感兴趣,他想到了一个问题。

牛牛想存n元钱,他决定第1天存1元,第2天存7元,第3天存49元,以此类推,每天存的钱是前一天的7倍。

牛牛想知道几天后,存款的总额能大于等于n元钱。
不会吧不会吧,这都不会

思路

被我吃了
直接暴力
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<iomanip>
using namespace std;
int main()
{
 int n;
 cin>>n;
 unsigned long long s=0,k=1,i=0;
 while (s<n)
 {
  s+=k;
  i++;
  k*=7;
 }
 cout<<i<<endl; 
 return 0;
}

B 平面旅行

题目

链接
来源:牛客网

牛牛最近在玩某款游戏,其地图可以看成一个平面直角坐标系。

地图上存在n个小镇,小镇从1到n编号。第i个小镇的坐标为(x_i,y_i)。定义两个小镇的距离为曼哈顿距离,比如小镇i到小镇j的距离为∣xi−xj∣+∣yi−yj∣,其中,|a|表示取a的绝对值。

牛牛在m个小镇建立了传送门,也就是说,牛牛可以在任何时候任何瞬间不花费任何代价,直接到达这m个小镇的任何一个。

牛牛一开始在小镇1,牛牛想按1到n的顺序访问所有小镇按顺序做任务,问牛牛需要走过的最短距离是多少。

牛牛可以提前到达某个小镇,但是必须做完前一个小镇的任务,才能做下一个小镇的任务。做任务本身不会增加距离。

范围

对于20%的数据有m=0。
对于40%的数据有m=1。
对于60%的数据有n,m≤300。
对于100%的数据有1≤n≤5000,0≤m≤n,−10000≤xi,yi≤10000。

思路

可以发现对于每一个有传送门的点都不用处理,而每一个没有传送门的点都由一个有传送门的点或上一个点步行过去(自己证)
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
struct f{
 int x,y;
} f[5001];
int op[5001];
long long a;
bool book[5001];
long long o(int x,int y,int x2,int y2)
{
 return abs(x-x2)+abs(y-y2);
}
int main()
{
 int n,m;
 scanf("%d%d",&n,&m);
 for (int i=1;i<=n;i++)
 {
  scanf("%d%d",&f[i].x,&f[i].y);
 }
 int x;
 unsigned long long s=0;
 for (int i=1;i<=m;i++)
 {
  scanf("%d",&x);
  op[i]=x;
  book[x]=1;
 }
 for (int i=1;i<n;i++)
 {
  if (book[i+1]) continue;
  a=o(f[i].x,f[i].y,f[i+1].x,f[i+1].y);
  for (int j=1;j<=m;j++)
  {
   a=min(a,o(f[i+1].x,f[i+1].y,f[op[j]].x,f[op[j]].y));
  }
  s+=a;
 }
 cout<<s<<endl;
 return 0;
}

C 小球下落

题目

链接
来源:牛客网

有一块大小为n行2列的板子,每个位置可能是一个小球,用’o’表示,可能是障碍物,用’x’表示,也可能空无一物,用’.'来表示。

每个小球可以向左向右或者向下移动,但是不能向上移动,或者和某个小球重叠,也不能越出板子。

每个小球向下移动一个单位,牛牛可以获得一分。

牛牛想知道对于某个开始状态,能得到的最大分数是多少。

范围

对于20%的数据有n≤3。

另有20%的数据k≤10。

对于60%的数据有n≤1000。

对于100%的数据有1≤n≤300000。

思路

如此人才的一道题目怎么能不暴力呢??
纯种暴力,不添加任何优化,坚持在原产地鲜榨
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n;
char a[300001][2];
int s=0;
int dfs(int x,int y,int dep)
{
    if (x==n) return dep;
    if (a[x+1][y]!='.'&&(a[x+1][1-y]!='.'||a[x][1-y]!='.'))
    {
        return dep;
    }
    int xx=0;
    if (a[x+1][0]=='.') xx=dfs(x+1,0,dep+1);
    if (a[x+1][1]=='.') xx=max(dfs(x+1,1,dep+1),xx);
    return xx;
}
int main()
{
    int n;
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for (int i=n;i>=1;i--)
    {
        if (a[i][0]=='o')
        {
            a[i][0]='.';
            int x=dfs(i,0,0);
            s+=x;
            x+=i;
            if (a[x][1]=='.'&&a[x][0]=='.')
            {
                if (a[x-1][0]=='x') a[x][0]='o';
                else a[x][1]='o';
            }
            else
            {
                if (a[x][1]=='.') a[x][1]='o';
                else
                {
                    a[x][0]='o';
                }
            }
        }
        if (a[i][1]=='o')
        {
            a[i][1]='.';
            int x=dfs(i,1,0);
            s+=x;
            x+=i;
            if (a[x][1]=='.'&&a[x][0]=='.')
            {
                if (a[x-1][0]=='x') a[x][0]='o';
                else a[x][1]='o';
            }
            else
            {
                if (a[x][1]=='.') a[x][1]='o';
                else
                {
                    a[x][0]='o';
                }
            }
        }
        if (a[i][0]=='o')
        {
            a[i][0]='.';
            int x=dfs(i,0,0);
            s+=x;
            x+=i;
            if (a[x][1]=='.'&&a[x][0]=='.')
            {
                if (a[x-1][0]=='x') a[x][0]='o';
                else a[x][1]='o';
            }
            else
            {
                if (a[x][1]=='.') a[x][1]='o';
                else
                {
                    a[x][0]='o';
                }
            }
        }
    }
    cout<<s<<endl;
 return 0;
}

D 自由世界

题目

链接
来源:牛客网

牛牛最近在玩某款游戏,其地图不能看成一个平面直角坐标系,而类似于一张无向图。

地图上存在n个小镇,小镇从1到n编号。有m条道路连接两个小镇,每条道路有其长度w_i​。

牛牛在k个小镇建立了传送门,也就是说,牛牛可以在任何时候任何瞬间不花费任何代价,直接到达这k个小镇的任何一个。

牛牛一开始在小镇1,牛牛想按1到n的顺序访问所有小镇按顺序做任务,问牛牛需要走过的最短长度是多少。

牛牛可以提前到达某个小镇,但是必须做完前一个小镇的任务,才能做下一个小镇的任务。做任务本身不会增加长度。

范围

对于10%的数据有n≤3,k=0。
对于30%的数据有k=0。
另有30%的数据有m=n-1。
对于60%的数据有n≤300。
对于100%的数据有1≤n≤2000,n−1≤m≤5000,0≤k≤n,1≤ui,vi≤n,1≤wi≤1000。
数据保证给定的小镇两两相互可达。
注意,连接某两个小镇的可能有多条道路,也有可能有道路的两端是同一个小镇。

思路

虽然正解是dij,但是……(我绝不会告诉你们我是spfa过的,而且机房巨佬犯罪嫌疑人lyf68ms的spfa在10.30还是最优解
首先可以把也有可能有道路的两端是同一个小镇 的这种乡村风景观光旅行边给去掉,然后,我们的任意门小镇可以直接进入spfa的队列中(反正做了第一次以后都要进去的),就不用多建边了.
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int head[2001],d[2001],n,m,k,tot=1;
queue<int> q;
struct f{
    int to,next,w;
} b[10001];
bool book[2001];
void add(int x,int y,int z)
{
    b[tot].to=y;
    b[tot].w=z;
    b[tot].next=head[x];
    head[x]=tot;
    tot++;
}
void spfa(int x)
{
    q.push(x);
    for (int i=1;i<=n;i++)
    {
     if (book[i]) d[i]=0,q.push(i);
     else d[i]=2000*1000+20;
 }
 d[x]=0;
    while (q.size())
    {
     int xx=q.front();
     q.pop();
     for (int i=head[xx];i;i=b[i].next)
     {
      if (d[b[i].to]>d[xx]+b[i].w)
      {
       d[b[i].to]=d[xx]+b[i].w;
       q.push(b[i].to);
   }
  }
 }
}
int main()
{
    cin>>n>>m>>k;
    for (int i=0;i<m;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        if (x==y) continue;
        add(x,y,z);
        add(y,x,z);
    }
    for (int i=0;i<k;i++)
    {
        int x;
        cin>>x;
        book[x]=1;
    }
    long long s=0;
    for (int i=1;i<n;i++)
    {
     if (book[i+1]) continue;
        spfa(i);
        s+=d[i+1];
    }
    cout<<s<<endl;
 return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值