2021-2022年度第三届全国大学生算法设计与编程挑战赛 (夏季赛)个人题解

前言

最近也那halo做了个个人博客,欢迎小伙伴来玩,虽然现在内容较少,但以后会多的(保证不咕咕咕,我逃)
博客地址

一 Poly

1.题目

在这里插入图片描述

2.思路

首先明确一点坐标点都是整数点且周长为整数:即任意一条边长都为整数
边尽可能少:只用枚举划分成三角形或者四边形的情况(我猜的,但对了2333)
然后就直接分情况讨论即可
1.C==3时,只有1,1,1的三角形,所以ans=0;
2.C>3&&C%2=1时,因为周长为奇数,所以至少是存在一条边(奇数条)的长度为奇数。我们随便取一条边L,设端点分别为(x1,y1)与(x2,y2) ,所以边长L^2 =(x1-x2)^2 +(y1-y2)^2;
若L为偶数,则|x1-x2|与|y1-y2|同偶or同奇,所以从(x1,y1)走到(x2,y2),移动点的x+y的奇偶不会发生改变
同理可得L为奇数的时候会发生改变
所以我们让移动点绕多边形转圈圈可以发现如果存在这样的多边形x+y的奇偶会发生改变,但现实是我们在转圈圈,奇偶并不会发生改变,所以可证得这样的多边形不存在
3.所以综上我们可得到只存在周长为偶数且大于3的多边形,观察数据范围是【3,5000】,可以直接暴力枚举第一象限,这里我规定的是一个点为原点,枚举另外两个点

3.代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include <algorithm>
using namespace std;
const int N=2e5+10;

int c,c_cur,max_L,min_L,w,cur,res=0x3f;
int x[N],y[N],l[N],idx=0;
bool can_line(int i, int j) {
    if(cur*cur==w&&cur<=c_cur&&l[i]+l[j]==c-cur&&x[i]*y[j]>y[i]*x[j]){
        return true;
    }else
        return false;
}

int dis(int i,int j) {
    w=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
    return sqrt(w);
}

int main() {
    cin>>c;
    if(c%2==1||c==3){ 
        cout<<"-1"<<endl;
        return 0;
    }
    c_cur=c>>1;
    for(int i=1;1<=c;i++){
        for(int j=0;i*i+j*j<=c_cur*c_cur;j++){
            cur=sqrt(i*i+j*j);
            if(cur*cur==i*i+j*j){
                l[++idx]=cur,x[idx]=i,y[idx]=j;
            }
        }
    }
    for(int i=1;i<=idx;i++){
        for(int j=i+1;j<=idx&&l[i]+l[j]<c;j++){
            cur=dis(i,j);
            if(can_line(i,j)){
                min_L=min(cur,min(l[i],l[j]));
                max_L=max(cur,max(l[i],l[j]));
                res=min(max_L-min_L,res);
            }
        }
    }
    if(res==0x3f3f3f3f){
        if(c%4==0) res=0;
        else
            res=1;
    }
    cout<<res;
    return 0;
}

二 Preview

1.题目

在这里插入图片描述

2.思路

一眼dp,但是那个时候还没听y总的dp降维,二维dp的o(n^2)炸了
f[i][j]:表示复习后i门课程,从第j天开始的总价值(i>=j)
(降维我这里就不细说了,具体看别的文章,因为我也讲不太明白,自己都是玄学dp,hhhh)

3.代码

	for(int i=n;i>=1;--i)
        for(int j=i-a[i];j<=i+a[i];++j)
        {
            if(j<0) 
                continue;
            dp[j]=dp[j];
            if(j<=i)
                dp[j]=max(dp[j],dp[i+a[i]+1]+a[i]*b[i]); 
        }

三 String

1.题目

在这里插入图片描述

2.思路

累加匹配的后缀,因为只能往队头移动

3.代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int n;
string s1,s2;

int main(){
    cin>>n;
    cin>>s1>>s2;
    s1="#"+s1;
    s2="#"+s2;
    int m=n,ans=0;
    for(int i=n;i>=1;i--){
        if(s1[i]==s2[m]){
            ans++;
            m--;
        }
    }
    cout<<n-ans;
    return 0;
}

四 Travel

1.题目

在这里插入图片描述

2.思路

两个思路:

  • ①分层图最短路(注意有的题会卡spfa,我觉得用dijkstra稳妥点)

  • ②dp+dijkstra

这里就说一下分层图的做法:分层图的核心我个人理解就是把原图平行k次,多添加了上一层到下一层的费用为0的边,然后和普通的最短路问题一样了
这里注意可能k次免费可能并没有用完

3.代码

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;

typedef pair<int,int> PII;

const int N=2e7+10;
int h[N],e[N],w[N],ne[N],idx;
int dis[N],min_val=N;
bool st[N];
int n,m,k;

struct yz{
    int y,z;
};

bool operator < (yz i,yz j)
{
	if((i.z-1)/n!=(j.z-1)/n)
        return (i.z-1)/n>(j.z-1)/n;
	return i.z>j.z;

}
void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

void Dijkstra(){
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;

    priority_queue<yz> heap;
    heap.push(yz{1,0});

    while(heap.size()){
        int t=heap.top().y;
        heap.pop();

        if(st[t]) continue;
        st[t]=true;
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(dis[j]>dis[t]+w[i]){
                dis[j]=dis[t]+w[i];
                heap.push(yz{j,dis[j]});
            }
        }
    }
}

int main(){
    memset(h,-1,sizeof h);
    cin>>n>>m>>k;
    int x=m;
    while(x--){
        int a,b,c;
        cin>>a>>b>>c;
        for(int i=0;i<=k;i++){
             add(i*n+a,i*n+b,c),add(i*n+b,i*n+a,c);
        }
        for(int i=0;i<k;i++){
             add(i*n+a,(i+1)*n+b,0),add(i*n+b,(i+1)*n+a,0);
        }
    }
    Dijkstra();
    for(int i=k;i>=0;i--){
       min_val=min(min_val,dis[i*n+n]);
    }
    cout<<min_val<<endl;
    return 0;
}

五 大富翁

1.题目

在这里插入图片描述

2.思路

签到题

3.代码

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;
const int N=2e5+100;
bool f[N];
int arr[N];

int main()
{
    f[1]=true;
    int n;
    cin>>n;
    for(int i=1;i<=n;++i){
        scanf("%d",&arr[i]);
    }
    for(int i=1;i<=n;++i){
        if(f[i])for(int k=1;k<=arr[i];++k){
            f[i+k]=1;
        }
    }
    int ans=0;
    for(int i=1;i<=N-10;++i){
        if(f[i])ans=max(ans,i);
    }
    cout<<ans<<endl;
    return 0;
}

六 真假英雄

1.题目

在这里插入图片描述

2.思路

并查集的题,也比较划水,可以去做做洛谷上的食物链那题

3.代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int fa[N],sz[N],n,m,ans;
int read(){
	int x=0,w=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*w;
}

int find(int x){
	if(fa[x]==x)return fa[x];
	fa[x]=find(fa[x]);
	sz[fa[x]]+=sz[x];
	sz[x]=0;
	return fa[x];
}

void mer(int x,int y){
	int fx=find(x),fy=find(y);
	if(fx!=fy){
		sz[fx]+=sz[fy];sz[fy]=0;
		fa[fy]=fx;
	}
}
//以上是并查集 
int main(){
	n=read();m=read();ans=0;
	for(int i=1;i<=2*n;i++)fa[i]=i;
	for(int i=1;i<=n;i++)sz[i]=0;
	for(int i=1+n;i<=2*n;i++)sz[i]=1;//预处理 
	for(int i=1;i<=m;i++){
		int x=read(),y=read();
		int fx=find(x),fy=find(y),fxn=find(x+n),fyn=find(y+n);//假定x,y表示坏人,x+n,y+n表示好人 
		string ch;cin>>ch;
		if(ch=="good"){
			mer(x,y);mer(y+n,x+n);//如果x说y是好人,则x,y要么同时是好人,要么同时是坏人 
		}else {
			mer(x+n,y);mer(y+n,x);//如果x说y是坏人,则x,y一定有一好一坏的情况 
		}
	}
	for(int i=1;i<=n;i++){
		if(find(i)==find(i+n)){cout<<-1<<endl;return 0;}//如果出现了i既是好人又是坏人的情况,则无解 
	}
	for(int i=1;i<=n;i++){
		ans+=max(sz[find(i)],sz[find(i+n)]);//统计答案 ,取max是因为贪心取最大就好,因为两个不同条件的成立带来的影响是不一样的 
		sz[find(i)]=sz[find(i+n)]=0;//注意清0,因为之前已经统计过答案了 
	}cout<<ans<<endl;
	return 0;
}
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值