CSP-J模拟赛(3)补题报告

前言:

          1.IP地址(ip):100

           2.是否同构(same):30

           3,箱子(box):10

           4.社恐的聚会(party):0

总结一下:创造新高(@o@),连续两天的flag都立住了。就是文本操作不太精通,C++卡了不知道怎么修~( 考试最后10分钟补的),下次肯定没这次高。这次第一题简单,下次再A一道题就行。

IP地址

题意:

我们有 N 个设备,每个设备都有它的名称和IPv4地址,现在我们有 Q 个问题,每次我们想知道给出的IPv4地址是哪一个设备?

考试回顾:

挺简单的,AC了(C++卡了,重测了一下)

题解:

MAP映射
 

AC: 

#include<bits/stdc++.h>
using namespace std;
int main(){
	//freopen("ip.in","r",stdin);
	//freopen("ip.out","w",stdout);
   map<string,string>mp;
   int n,m;
   string mc,ym;
   cin>>n;
   for(int i=1;i<=n;i++){
   	  cin>>mc>>ym;
   	  mp[ym]=mc;
   }
   cin>>m; 
   for(int i=1;i<=m;i++){
   	cin>>mc;
   	cout<<mp[mc]<<"\n";
   }
	return 0;
}

是否同构

题意:

有两个长度为 N的数组 a,b,我们想知道数组 a 和数组 b 是否是同构数组?
我们定义两个数组 a,b 同构,则存在一个整数 k,使得 0≤k≤⌊N2⌋,有保持数组 b 不动的时候,交换数组 a 的前 k 项和后 k 项交换位置,即 swap(a1,aN−k+1,⋯,swap(ak,aN)),使得新的数组 a完全相等于数组 b。

考试回顾:

重点攻克这一道题,花了一个半小时做出来,但是时超了~
 

题解:


观察到 a和 b都是一个排列,然后我们可以找到 b1在 数组a中出现的位置(记为pos ),然后我们整体交换 a1~pos-1和 apos~n,再判断 a和 b是否相同即可。

AC: 

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int T,n,a[N],b[N];
bool check(){
    for(int i=1;i<=n;i++){
        if(a[i]!=b[i]) return 0;
    }
    return 1;
}
int main(){
	//freopen("party.in","r",stdin);
    //freopen("party.out","w",stdout);
	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(check()){
	        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(check()) printf("Yes\n");
	    else printf("No\n");
	}
	return 0;
}

箱子

题意:

我们有 N 个箱子,每个箱子有自己的重量 w​i​​,每次我们可以将至多 M 个箱子合并成一个重量为这几个箱子重量和的箱子,花费的体力是这合并的几个箱子的重量和。请问我们将这所有的箱子合并成一个箱子所需要花费的最少体力是多少?

考试回顾:

想到了用小顶堆,但没考虑到不足m个的情况。对于我来说,第三题拿到十分算比较不错的了;
 

题解:

可以想到,每次合并最小的箱子是最优的,因此不断合并最小的 m个箱子后,重新排序得到新序列,然后继续合并最小的 m个箱子。

AC: 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
//freopen("box.in","r",stdin);
//freopen("box.out","w",stdout);
	ios::sync_with_stdio(false);
	int n,m;
	cin>>n>>m;
	priority_queue<ll,vector<ll>,greater<ll> > pq;
	for(int i=1; i<=n; i++) {
		int x;
		cin>>x;
		pq.push(x);
	}
	if((n-1)%(m-1)>0) {
		int cnt=m-1-(n-1)%(m-1);
		while(cnt--) {
			pq.push(0);
		}
	}
	ll ans=0;
	while(pq.size()>1) {
		ll res=0;
		for(int j=1; j<=m; j++) {
			res+=pq.top();
			pq.pop();
		}
		ans+=res;
		pq.push(res);
	}
	cout<<ans<<endl;
	return 0;
}

社恐的聚会

题意:

有 N个患有社交恐惧症的人想参与一个聚会,但是这个聚会只有两张桌子,这些社恐们不想跟自己不认识的人坐在一起!

你是这次聚会的主办方,请你想想办法,看看能不能将这 N 个人分在两张桌子,使得每张桌子的任意两个人都是相互认识的。

如果你有办法让这 N个人分在两张桌子,请你再想想办法看看能不能让这两张桌子中人数最多的一张桌子的入座人数最少呢?

考试回顾:

完全不会,骗分得了10分,但是编译错误了。

题解:

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

AC: 

#include<bits/stdc++.h>
#define ll long long;
using namespace std;
const int N=514;
int n,m,cnt,a[N][N],sz[N][2],col[N];
bool f[N];
bool vis[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 0;
            }
            else if(!dfs(i,1-c))  return 0;
        }
    }
    return 1;
}
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个

红包金额最低5元

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

抵扣说明:

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

余额充值