CSP-J 2024模拟赛3

分数

T1:IP地址(ip)        100

T2是否同构(same)        50

T3:箱子(box)        10

T4:社恐的聚会(party)        10

总分:170

题解

T1:IP地址(ip)

题意:

N个设备,每个设备都有它的名称和地址,有Q次询问,每次询问输出给出的地址指向哪一个设备?

赛时总结:

一眼,5分钟速通。

题解:

拿map标记一遍,最后直接输出就行。

AC代码:

#include<bits/stdc++.h>
using namespace std;
int n,q;
map<string,string> mp;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		string a,b;
		cin>>a>>b;
		mp[b]=a; 
	}
	cin>>q;
	for(int i=1;i<=q;i++){
		string a;
		cin>>a;
		cout<<mp[a]<<'\n';
	}
	return 0;
}

T2是否同构(same)

题意:

有两个长度为N的数组a,b,我们想知道数组a和数组b是否是同构数组?

定义两个数组a,b同构,则存在一个整数k,使得0\leq k\leq \left \lfloor \frac{N}{2} \right \rfloor,保持数组b不改动,交换数组a的前k项和后k项交换位置后,使a=b

赛时总结:

直接写了暴力,TLE卡了50分。

思路:

我们可以找到b_{1}a数组中出现的位置(记为pos),然后整体交换a_{pos\sim n}a_{1\sim n-pos+1},再判断ab是否相同即可。 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int T,n,a[1000005],b[1000005],t[1000005];
bool fun(){
	for(int i=1;i<=n;i++){
		if(a[i]!=b[i]){
			return 0;
		}
	}
	return 1;
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d",a+i);
		}
		for(int i=1;i<=n;i++){
			scanf("%d",b+i);
		}
		if(fun()){
			printf("Yes\n");
			continue;
		}
		int pos=(n>>1)+1;
		for(;pos<=n;pos++){
			if(a[pos]==b[1])	break;
		}
		for(int i=pos;i<=n;i++){
			swap(a[i],a[i-pos+1]);
		}
		if(fun())	printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

T3:箱子(box)

题意:

N个箱子,每个箱子的重量为w_{i},每次可以将至多M个箱子合并成一个重量为这几个箱子重量和的箱子,花费的体力是这几个箱子的重量和。求出将所有箱子合并成一个箱子所花费的最少体力。

赛时总结:

第一眼:合并果子        10分后:?        讲完后:完了理解错题了。

思路:

可以想到,每次合并最小的箱子是最优的,但是不一定每次合并都一定正好有M个箱子。

所以

1.当(n-1) mod (m-1) = 0,在这种情况时,我们每次取M个合并最终可以刚好合并成一个,每次取最小的 个合并即可。

2.当(n-1) mod (m-1) \neq 0,在这种情况时,一定会发生一次当前剩余数量不够M个箱子合并的情况,可以补足若干个0,使得其满足(n-1) mod (m-1) = 0,再按照1的办法进行合并。

AC代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,sum;
priority_queue<ll,vector<ll>,greater<ll> > pq;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		pq.push(x);
	}
	while((pq.size()-1)%(m-1))	pq.push(0);
	while(pq.size()>1){
		ll cnt=0;
		for(int i=1;i<=m;i++){
			if(!pq.size())	break;
			cnt+=pq.top();
			pq.pop();
		}
		sum+=cnt;
		pq.push(cnt);
	}
	cout<<sum;
	return 0;
}

T4:社恐的聚会(party) 

题意:

N个患有社交恐惧症的人参与一个聚会,但聚会只有两张桌子,这些社恐们不想跟自己不认识的人坐在一起,能不能将这N个人分在两张桌子,使得每张桌子的任意两个人都是相互认识的。

赛时总结:

直接输出No得了十分,题是一点没看懂。

思路:

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

dp_{i,j,0}表示前i个连通块,是否能塞入j个点到第一张桌子(白点)。

dp_{i,j,1}表示前i个连通块,是否能塞入j个点到第二张桌子(黑点)。

每个连通块的黑点和白点是可以互换的

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=514;
int n,m,cnt,a[N][N],sz[N][2],col[N];
bool vis[N],f[N];
bool dfs(int u,int c){
	vis[u]=1,col[u]=c,sz[cnt][c]++;
	for(int i=1;i<=n;i++){
		if(i!=u&&(!a[i][u]||!a[u][i])){
			if(vis[i]){
				if(col[u]==col[i])	return false;
			}
			else if(!dfs(i,1-c))	return false;
		}
	}
	return true;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&a[i][j]);
		}
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			cnt++;
			if(!dfs(i,0)){
				printf("No\n");
				return 0;
			}
		}
	}
	m=n>>1;
	f[0]=1;
	for(int i=1;i<=cnt;i++){
		for(int j=m;;j--){
			if(j<sz[i][0]&&j<sz[i][1])	break;
			if(j>=sz[i][0])	f[j]|=f[j-sz[i][0]];
			if(j>=sz[i][1])	f[j]|=f[j-sz[i][1]];
		}
	}
	for(int j=m;j>=1;j--){
		if(f[j]){
			printf("Yes\n%d",n-j);
			break;
		}
	}
	return 0;
}

总结

第三题还是不够细心,但至少还有10分,第四题如果加个特判还能骗30分,希望下次能沉下心来,早日​​​AKJ组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值