CSP-J第三场模拟赛补题报告2024.10.3

看我博客的人越来越多力(喜

一、题目报告

第一题AC,第三题10pts。比赛后AK

二、比赛概况

10min前:死机了一台,不过还没编多少问题不大

花5minAC了第一道,看到了第三题,看出了贪心,利用优先队列去做

第四题一看不会,去做第二题,磕了1h30min磕出来半成品(事后发现错得离谱)

在3h的时候电脑又死机了,代码全无,最后老师给的时间把代码想着搓了一遍。预估200,实际110(第三题做法假了)

三、解题报告 

T1 IP地址(ip)

得分情况

比赛时AC。

题目大意

给定n个网址,每个网址对应一个IP地址,给定m个网址(都已出现过),问该IP地址对应哪一个网址。

解题思路

一个网址对应一个IP地址,很容易想到映射容器。

正解代码 

#include<bits/stdc++.h>
using namespace std;
map<string,string>mp;
int main(){
	int n,m;
	cin>>n;
	string s,s1;
	for(int i=1;i<=n;i++){
		cin>>s>>s1;
		mp[s1]=s;
	}
	cin>>m;
	while(m--){
	    cin>>s;
	    cout<<mp[s]<<endl; 
	} 
	return 0;
} 

T2 是否同构(same)

得分情况

比赛时0pts。

错因是没有想到思路,使用了复杂的队列。

题目大意

给定两个长度为 n 的数组 a ,b ,若存在正整数 k\leqslant \left \lfloor n/2 \right \rfloor,使 a 数组前 k 个元素与后 k 个元素交换后全部等于 b 数组,则称a,b数组为同构数组。问a,b是否是同构数组。保证数组内无重复元素

解题思路

我们遍历一遍a数组,找到其中与b[1]相同的元素,标记位置。从标记位置到末端依次与前面元素交换,最后判断是否相等。

需要特判数组原本就相等的情况。

正解代码

#include<bits/stdc++.h>
using namespace std;
int a[114514],b[114514],n,m,t;
bool check(){
	for(int i=1;i<=n;i++){
		if(a[i]!=b[i])return 0;
	}
	return 1;
}
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		for(int i=1;i<=n;i++)scanf("%d",&b[i]);
		if(check()){
			printf("Yes\n");//特判
			continue;
		}
		int pos=0;
		for(int i=n/2+1;i<=n;i++){
			if(a[i]==b[1]){
				pos=i;//找到交换起始点
				break;
			}
		}
		for(int i=pos;i<=n;i++){
			swap(a[i],a[i-pos+1]);//与前面元素交换
		}
		if(check()){
			printf("Yes\n");
			continue;
		}
		else printf("No\n");
	}
    return 0;
}

T3 箱子(box)

得分情况

比赛时10pts。

错因是没有特判,使用了另一种问题的思路。

题目大意

石子合并,但是一次可以合并m堆,求最小代价

解题思路

本题使用贪心算法。利用小根优先队列把较轻的石子浮在上面,依次合并。需要注意的是,如果可能出现剩余堆数少于m的情况,要进行特判(没有特判损失90pts)

正解代码 

#include<bits/stdc++.h>
using namespace std;
priority_queue<long long,vector<long long>,greater<long long> >q;//小根堆
long long ans,n,m,a[114514];
int main() {
	cin>>n>>m;
	for(int i=1; i<=n; i++) {
		cin>>a[i];
		q.push(a[i]);
	}
	if((n-1)%(m-1)>0) {
		int cnt=m-1-(n-1)%(m-1);//特判
		while(cnt--) {
			q.push(0);
		}
	}
	while(q.size()>1) {
		long long dm=0;
		for(int i=1; i<=m; i++) {
			dm+=q.top();
			q.pop();
		}
		ans+=dm;
		q.push(dm);
	}
	cout<<ans;
	return 0;
}

T4 社恐的聚会(party)

得分情况

比赛时0pts。

错因是没有掌握相关知识。

题目大意

有 n 个社恐参加聚会,只有两张桌子,每个社恐希望与自己相互认识的人坐在一张桌上。给定社恐们的认识关系,问能否使每个社恐满足需求,如果可以,那么人数多的那张桌子坐的人要尽可能少。输出是(那么还要输出就座人数最多的桌子的人数)或否。

(认识关系可能不是相互的。i认识j,但j不一定认识i,有向图)

解题思路 

把所有不互相认识的人之间连一条无向边,然后可以对于所有连通块判断当前连通块是否是二分图(使用黑白染色法),如果不是二分图,则直接输出No,否则可以记录一下每个连通块中黑点的数量和白点的数量(代表在第一张桌子还是第二张桌子)。注意,黑点和白点本质没有任何区别,每个连通块都可以黑白互换。然后求得每个连通块的黑点和白点数量后,可以做一遍判定性背包dp来求解答案。 

正解代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=525;
struct graph {///结构体
	int head[maxn],nxt[maxn*maxn],to[maxn*maxn],cnt;
	inline graph():cnt(1) {} inline void add(int u,int v) {//加边函数
		nxt[++cnt]=head[u];
		to[cnt]=v;
		head[u]=cnt;
	}
} gr;
int n,g[maxn][maxn];
bool vis[maxn];
int color[maxn],sz[maxn][2],idx;
bool dfs(int u,int c) {  //dfs图,判断是否是二分图
	vis[u]=true;
	color[u]=c;
	sz[idx][c]++;
	for(int i=gr.head[u]; i; i=gr.nxt[i]) {
		int v=gr.to[i];
		if(vis[v]) {
			if(color[u]==color[v]) {
				return false;
			}
		} else {
			if(!dfs(v,c^1))return false;
		}
	}
	return true;
}
bool dp[maxn][maxn][2];
int main() {
	cin>>n;
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=n; j++) {
			cin>>g[i][j];
		}
	}
	for(int i=1; i<n; i++) {
		for(int j=i+1; j<=n; j++) {
			if(!g[i][j]||!g[j][i]) {
				gr.add(i,j);//加边
				gr.add(j,i);
			}
		}
	}
	for(int i=1; i<=n; i++) {
		if(vis[i])continue;
		idx++;
		if(!dfs(i,0)) {
			cout<<"No"<<endl;
			return 0;
		}
	}
	dp[0][0][0]=true;//存在性DP
	dp[0][0][1]=true;
	int mx=n/2;
	for(int i=1; i<=idx; i++) {
		for(int j=sz[i][0]; j<=mx; j++) {
			dp[i][j][0]|=dp[i-1][j-sz[i][0]][0];
			dp[i][j][0]|=dp[i-1][j-sz[i][0]][1];
		}
		for(int j=sz[i][1]; j<=mx; j++) {
			dp[i][j][1]|=dp[i-1][j-sz[i][1]][0];
			dp[i][j][1]|=dp[i-1][j-sz[i][1]][1];
		}
	}
	int ans=0;
	for(int j=mx; j>=1; j--) {
		if(dp[idx][j][0]||dp[idx][j][1]) {
			ans=n-j;
			break;
		}
	}
	cout<<"Yes"<<endl;
	cout<<ans<<endl;
	return 0;
}

四、总结

本场比赛中我出现了一些状况,我没有及时应变,错过了好多分数,在T3中还因细节错过90分。希望巩固学过算法,注重细节方面,学会及时调整自己。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值