SDKD_ACM2021新生赛题解

A - JiaoZhu的粉丝

  • 签到题。根据输入使用循环输出。
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
	int n;
	cin>>n;
	cout<<"UserName-------GY"<<endl;
	for(int i=1;i<=n;i++)
	cout<<"    JiaoZhuV5"<<endl;
	cout<<"-----2021/10/24----";
	return 0;
}

B - Vvvvvegetable(by gy)

  • 可以用一个 12x3 大小的桶(实际用时要稍微开大一点避免越界)来维护相应进度的玩家数量,然后查找排名第 n/2 的玩家进度,对于比该进度低的询问输出“They are very Vvvvvegetable.”,否则输出相应玩家的个数。
#include <bits/stdc++.h>
using namespace std;
int tong[15][5];//稍微开大一些,避免越界
int main()
{
    int n,a,b,mid_a,mid_b;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&a,&b);
        tong[a][b]++;//桶存储相应进度人数
    }
    int sum=0;
    for(int i=12;i>=1;i--)
        for(int j=3;j>=1;j--)
        {
            if(sum<n/2)//如果到当前为止人数加起来还不够50%
            {
                mid_a=i;//更新标记
                mid_b=j;
            }
            sum+=tong[i][j];
        }
    int q;
    cin>>q;
    while(q--)
    {
        scanf("%d %d",&a,&b);
        if(a>mid_a||(a==mid_a&&b>=mid_b))//和标记比较大小
            printf("%d\n",tong[a][b]);
        else
            printf("They are very Vvvvvegetable.\n");
    }
    return 0;
}

C - LHL爬网线(by lhl)

在这里插入图片描述

D - 铺地毯

  • 求的是给定位置最上面的地毯,将输入保存后倒着遍历即可。
#include<iostream>
using namespace std;
int main()
{
	int n,x,y,xx,yy;
	cin>>n;
	int car[n+1][5];
	for(int i=1;i<=n;i++)
	for(int j=1;j<=4;j++)
	cin>>car[i][j];
	cin>>x>>y;
	for(int i=n;i>=1;i--)
	{
		xx=car[i][1]+car[i][3];
		yy=car[i][2]+car[i][4];
		if(car[i][1]<=x&&car[i][2]<=y&&xx>=x&&yy>=y)
		{
			cout<<i;
			return 0;
		}
		
	}
	cout<<"-1";
	return 0;
}

E - 马的遍历

  • bfs,从起始点开始向外bfs,保存每个位置第一次到达时的步数。
  • dfs与bfs算法
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int x,y,m,n,ans[405][405],xx[8]={-1,-1,1,1,2,2,-2,-2},yy[8]={2,-2,2,-2,1,-1,1,-1};
struct node{
	int x,y,step;
};
int main()
{
	cin>>n>>m>>x>>y;
	memset(ans,-1,sizeof(ans));
	queue<node>q;
	q.push({x,y,0});
	ans[x][y]=0;
	while(!q.empty())
	{
		node nw=q.front();
		q.pop();
		for(int i=0;i<8;i++)
		{
			int nx=nw.x+xx[i],ny=nw.y+yy[i];
			if(nx>0&&nx<=n&&ny>0&&ny<=m&&ans[nx][ny]==-1)
			{
				ans[nx][ny]=nw.step+1;
				q.push({nx,ny,ans[nx][ny]});
			 } 
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		printf("%-5d",ans[i][j]);
		cout<<endl;
	}
	return 0;
}

F - 选数

  • dfs,每次选到第k个数,如果当前和是素数,判断是否被标记过,未被标记答案加1。
  • dfs与bfs算法
   #include<iostream>
   #include<cmath>
   using namespace std;
   int k,ans,n,y[5000005];
   bool yy[5000005];
   inline bool prime(int x) //判断素数
    {
        for(register int i=2;i<=sqrt(x);i++)
            if(x%i==0)
                return false;
        return true;
    }
    void dfs(int a,int tmp,int m)//dfs,a:已经选完a个数。tmp:当前和
    {
        if(a==k)
        {
            if(prime(tmp))
            ans++;//答案
            return;
        }
        for(int i=m;i<=n;i++)//m:这一次从第几个数开始选,减少对相同选数方案的多次枚举
        if(!yy[i])
        {
            yy[i]=1;//标记第i个数被选过
            dfs(a+1,tmp+y[i],i+1);
            yy[i]=0;//回复标记
        }
    }
    int main()
    {
        cin>>n>>k;
        for(int i=1;i<=n;i++)
        {
           cin>>y[i];
        }
        dfs(0,0,1);
        cout<<ans;
        return 0;
    }

G - 忧郁的蘑菇

  • 不妨思考一下,在所有正方形中,面积相对较大的正方形存放难度肯定比面积较小的正方形高(大正方形能放的区域,小正方形一定可以,但是反之不行),所以可以按照面积从大到小的顺序存放正方形,又因为大区域刚好可以分割成 9 个小区域,我们可以利用深搜不断缩小区域面积,直到刚好等于当前需要的面积(请仔细思考为什么可以这么分割)。(如图把左边一个大区域分成 9 块,取出淡蓝色块再将其看做一个大区域…以此类推)

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e4+5;
int cnt,n;
struct e
{
    int val,id,ansx,ansy;
}air[maxn];
bool com(e a,e b)
{
    return a.val>b.val;
}
bool com2(e a,e b)
{
    return a.id<b.id;
}
void dfs(int l,int top,int r,int bottom)
{
    if(cnt>n)
        return ;
    if(air[cnt].val==r-l+1)
    {
        air[cnt].ansx=r+l>>1;
        air[cnt].ansy=top+bottom>>1;
        cnt++;
        return ;
    }
    else
    {
        int sonlen=(r-l+1)/3,ll=l,tt=top;
        for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            dfs(l+(i-1)*sonlen,tt+(j-1)*sonlen,ll+i*sonlen-1,tt+j*sonlen-1);
    }
}
int main()
{
//    freopen("3.in","r",stdin);
//    freopen("3.out","w",stdout);
    int r;
    cin>>r>>n;
    cnt=1;
    for(int i=1;i<=n;i++)
    {
        air[i].id=i;
        scanf("%d",&air[i].val);
    }
    sort(air+1,air+n+1,com);
        dfs(1,1,r,r);
    sort(air+1,air+n+1,com2);
    for(int i=1;i<=n;i++)
        printf("%d %d\n",air[i].ansx,air[i].ansy);
    return 0;
}

H - 难题

  • m=n,问题简化成了对单个数进行素数判断,判断n是不是素数只需要从2枚举到sqrt(n)看看有没有它的因子即可,主义,1不是素数。
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int main()
{
	int n,m;
	cin>>n>>m;
	if(n==1)
	{
		cout<<0;
		return 0;
	}
	m=sqrt(n);
	for(int i=2;i<=m;i++)
	{
		if(!(n%i))
		{
			cout<<0;
			return 0;
		}
	}
	cout<<n;
	return 0;
}

I - 营救

  • 方法一:二分或倍增 + bfs,二分最大拥挤度,bfs判断当前拥挤度是否可行。
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
struct edge{
    int v,w,next;
}e[1000005];
int n,m,s,fi[500005],gi[500005],wi[500005],cnt,front[20005],t,lefts,rights,mid;
long long d[10005],inf=2147483647;
inline void inserts(int u,int v,int w){
    cnt++;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=front[u];
    front[u]=cnt;
}
int inq[100001];
void spfa(){
    queue<int> q;
    memset(inq,0,sizeof(inq));
    for(int i=1;i<=n;i++)
    d[i]=inf;
    d[s]=0;
    inq[s]=1;
    q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();inq[u]=0;
        for(int i=front[u];i>=0;i=e[i].next){
            int v=e[i].v,w=e[i].w;
            if(d[u]+w<d[v]&&w<=mid){
                d[v]=d[u]+w;
                if(!inq[v]){
                    inq[v]=1;
                    q.push(v);
                }
            }
        }
    }
}

int main()
{
    memset(front,-1,sizeof(front));
    cin>>n>>m>>s>>t;
    for(int i=1;i<=m;i++)
    {
        cin>>fi[i]>>gi[i]>>wi[i];
        inserts(fi[i],gi[i],wi[i]);
        inserts(gi[i],fi[i],wi[i]);
        rights=max(rights,wi[i]);
    }
    while(lefts<=rights)
    {
         mid=(lefts+rights)>>1;
        spfa();
        if(d[t]==inf)
        lefts=mid+1;
        else
        rights=mid-1;
    }
    cout<<lefts;
    return 0;
}
  • 方法二:克鲁斯卡尔,每次新选择一条边通过并查集判断起点与目标点是否联通,初次联通时那此所插入的边的权值即为答案。
  • 最小生成树算法——Kruskal

J - 旅行 Tour

  • 双向TSP问题,通过DP解决。
  • 一去一回看成两个单向的去,d(i,j)表示当前前面在i后面在j时所走的最短距离(一般可以认为的加一些限制来方便求解),具体做法详见紫书动态规划篇。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const double maxn=0x3f3f3f3f;
int x[1005],y[1005],n;
double d[1005][1005];
double ojld(int a,int b,int c,int d)
{
	return sqrt((double)((a-c)*(a-c)+(b-d)*(b-d)));
}
int main()
{
	while(cin>>n&&n!=EOF)
	{
		for(int i=1;i<=n;i++)
		scanf("%d%d",&x[i],&y[i]);
		for(int i=0;i<=n;i++)
		for(int j=0;j<=n;j++)
		d[i][j]=maxn;
		d[n][n]=0;d[n][n-1]=ojld(x[n-1],y[n-1],x[n],y[n]);
		for(int i=n-1;i>=2;i--)
		for(int j=i-1;j>=1;j--)
		d[i][j]=min(d[i+1][i]+ojld(x[j],y[j],x[i+1],y[i+1]),d[i+1][j]+ojld(x[i],y[i],x[i+1],y[i+1]));
		printf("%.2lf\n",d[2][1]+ojld(x[1],y[1],x[2],y[2]));
	}
	return 0;
}

K - 归程

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈希表扁豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值