CSP-J第三套模拟题————————————————————李奕泽

一、题目报告:

题目:

IP地址
是否同构
箱子
社恐的聚会

得分:

100

30

0

10

二、赛中情况:

前两道题用了半个小时,第三道题以为是类似与石子合并,用了两个队列来做,但这道题本应该用优先队列做,而且还忘记考虑一种样例;第四题很难,要先建立一个二分图,然后用DP来求。

三、题目解析:

IP地址

【题目描述】

IP地址(Internet Protocol Address)是互联网上用于识别和定位设备的数字标识。它是一种由32位或128位二进制数字组成的地址,在IPv4和IPv6两个主要版本中使用。

IP地址的主要功能是标识和寻址设备,使其能够在互联网上进行通信。通过将IP地址分配给计算机、服务器、路由器和其他网络设备,数据包可以被正确地发送到目标设备。IP地址还用于确定网络中不同设备的位置,以便进行网络管理和故障排除。

总之,IP地址是互联网上用于标识和定位设备的数字标识,使设备能够在互联网上进行通信。IP地址的外观根据其版本而有所不同。以下是IPv4和IPv6两个主要版本的IP地址中IPv4地址示例:

  1. 192.168.0.1
  2. 172.16.254.1
  3. 10.0.0.1
  4. 208.75.57.100

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

【输入格式】

第一行,一个正整数 N ,表示有 N 个设备;

接下去 N 行,首先输入该设备的名称,数据保证该设备的名称只由英文大小写组成,其次再输入该设备的IPv4地址;

接下去一行,输入一个正整数 Q ,表示有 Q 次询问;

接下去 Q 行,每行一个IPv4地址。

【输出格式】

对于 Q 次询问,每次询问输出该IPv4地址对应的设备名称。

【输入样例 1】

  1. 4
  2. Main 192.168.0.1
  3. Google 8.8.8.8
  4. some 123.13.34.45
  5. other 23.32.45.54
  6. 3
  7. 192.168.0.1
  8. 23.32.45.54
  9. 8.8.8.8

【输出样例1】

  1. Main
  2. other
  3. Google

【数据范围】

对于30% 数据,1≤N,M≤100;

对于 100% 数据,1≤N,M≤1000,设备的名称是只由大小写字母组成的长度小于等于100的字符串,IPv4地址”a.b.c.d”满足0≤a,b,c,d≤255 。

AC代码:

#include<iostream>
#include<cstdio>
using namespace std;
struct Node{//结构体存储
	string name,ip;
}arr[1010];
int main(){
	int n;
	cin >> n;
	for(int i = 1;i <= n;i++){
		cin >> arr[i].name >> arr[i].ip;
	}
	int m;
	cin >> m;
	for(int i = 1;i <= m;i++){
		string s;
		cin >> s;
		for(int j = 1;j <= n;j++){//数据范围不大,直接查
			if(arr[j].ip == s){
				cout << arr[j].name << "\n";
				break;
			}
		}
	}
	return 0;
} 

 

是否同构

时间限制:1秒        内存限制:256M

【题目描述】

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

【输入格式】

第一行输入一个正整数 T ,表示有 T 组输入;
每组输入的第一行,输入一个正整数 N ;
第二行输入 N 个整数,表示数组 a;
第三行输入 N 个整数,表示数组 b。

【输出格式】

如果数组 a 和数组 b 同构,则输出Yes,否则,输出No

【输入样例 1】

  1. 3
  2. 3
  3. 1 2 3
  4. 3 2 1
  5. 4
  6. 3 1 2 4
  7. 4 3 1 2
  8. 5
  9. 2 3 1 4 5
  10. 5 3 1 4 2

【输出样例 1】

  1. Yes
  2. No
  3. Yes

【数据范围与约定】

对于 30% 的数据有,1≤N≤10。

对于 100% 的数据有,1≤T≤10;1≤N≤10​6​​;1≤a​i​​,b​i​​≤N,且有 a​i​​≠a​j​​(i≠j)。

#include<iostream>
#include<cstdio>
using namespace std;
int a[1000010],b[1000010];
bool b_p[1000010];
int main(){
	int T;
	scanf("%d",&T);
	while(T--){//10 * 
		int n;
		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]);
		}
		int pos = 0;
		for(int i = 1;i <= n;i++){//任何元素都会牵连到a[1],所以只要找到b数组中a[1]的位置就能知道移的长度和元素
			if(a[i] == b[1]){
				pos = i;
			}
		}
		for(int i = 1;i <= n-pos+1;i++) swap(a[i],a[i+pos-1]);
/*
a[pos]   a[pos+1]   ...   a[n]


a[1]     a[2]       ...   a[n-pos+1]
*/
		bool bl = 0;
		for(int i = 1;i <= n;i++){
			if(a[i] != b[i]){
				bl = 1;
				break;
			}
		}
		if(bl == 1){
			cout << "No\n"; 
		}else cout << "Yes\n";
	} 
	fclose(stdin);
	fclose(stdout);
	return 0;
} 

箱子

时间限制:1秒        内存限制:256M

【题目描述】

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

【输入格式】

第一行,两个正整数 N ,M ,表示有 N 个箱子,每次操作至多可以合并 M 个箱子。

第二行,N 个正整数,表示这 N 个箱子每个箱子的重量。

【输出格式】

输出一个正整数,表示我们最少需要花费的体力是多少。

【输入样例 1】

  1. 3 2
  2. 1 2 3

【输出样例 1】

9

【输入样例 2】

  1. 7 3
  2. 1 2 3 4 5 6 7

【输出样例 2】

49

【输入样例 3】

  1. 3 3
  2. 1 2 3

【输出样例 3】

6

【数据范围】

对于 30% 的数据,2≤M≤N≤10​3​​。

 

 先普及优先队列的知识:

**优先队列**
使用函数库实现,头文件<queue>

1)大根堆(默认):priority_queue<队列中元素类型>  队列名;

     小根堆:(实现可向元素*-1再入最大优先队列)

priority_queue<队列元素类型,vector<类型>,greater<类型>空格>    队列名;

如:priority_queue<int,vector<int>,greater<int> >    q;

2)常用操作

q.push(x)    将x入堆    O(logn)
q.pop()    将堆顶元素(取而不删)    O(logn)
q.top()    获取栈顶元素    O(1)
q.size()    队列内元素个数    O(1)
q.empty()==1    队列为空    O(1)

优先队列进阶

 

每次把 m个合并成 1个相当于减去了 m-1个 

(n-m)%(m-1)= 0这种情况我们每次取 个合并最终可以刚好合并成一个,然后我们
使用一个小根堆,每次取最小的 m个合并即可。
(n-m)%(m-1)\neq0,这种情况一定会发生一次当前剩余数量不够 m个的情况,那么我们
可以补足若干个 ,使得满足(n-1) % (m-1) = 0,然后我们按照上述方法做即可,此方法
等价于在第一次合并的时候少选几个箱子,以便让后面的合并可以每次都取 m个。

AC代码:

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
priority_queue< long long ,vector<long long>,greater<long long> > q;
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i = 1;i <= n;i++){
		int t;
		scanf("%d", &t);
		q.push(t);
	}
	long long ans = 0;
	if((n-m) % (m-1) != 0){
		long long k = (n-m) % (m-1),sum = 0;
		for(int i = 1;i <= k + 1;i++){
			sum += q.top();
			q.pop();
		} 
		q.push(sum);
		ans += sum; 
	}
	while(q.size() >= m){
		long long sum= 0;
		for(int i = 1;i <=m;i++){
			sum += q.top();
			q.pop(); 
		}
		q.push(sum);
		ans += sum;
	} 
	cout << ans;
	fclose(stdin);
	fclose(stdout);
	return 0;
} 
/*
7 3
1 2 3 4 5 6 7
6 
4 5 6 7 /// 6
6 15
7 /// 15
28 /// 28

9 
*/

社恐的聚会

时间限制:1秒        内存限制:256M

【题目描述】

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

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

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

【输入格式】

第一行输入一个整数 N ,表示有 N 个社恐。

接下去第 2 行至第 N+1 行,每行N 个为 0 或者 1 的整数,表示第 i−1 号人是否认识第 jj 号人,如果为 0 ,表示第 i−1号社恐不认识第 j号社恐,否则为 1 ,表示第 i−1i−1 号社恐认识第 j 号社恐。(可以是第 i 号社恐认识第 j 号社恐,但是第 j 号社恐不认识第 ii 号社恐)

【输出格式】

如果不能分成两张桌子坐下这 N 个人,请输出No

否则,先输出Yes,然后再输出一个正整数,表示将这 N 个人安排入座之后的两张桌子中人数最多的那张入座人数最少是多少。

【输入样例 1】

  1. 3
  2. 0 1 1
  3. 1 0 1
  4. 0 1 0

【输出样例 1】

  1. Yes
  2. 2

【输入样例 2】

  1. 3
  2. 0 0 0
  3. 0 0 0
  4. 0 0 0

【输出样例 2】

No

【输入样例 3】

  1. 4
  2. 0 1 1 1
  3. 0 0 1 1
  4. 0 1 0 1
  5. 1 0 0 0

【输出样例3】

  1. Yes
  2. 2

【数据范围】

对于30% 的数据,1≤N≤20;

对于 100% 的数据,1≤N≤512, a[i][i]=0a[i][i]=0,其中 a[i][i]a[i][i] 表示第i号社恐对自己的是否认识为“不认识”。 

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

二分图

背包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){
    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(){
    ios::sync_with_stdio(false);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[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;
}

各位大佬加油!!!本蒟蒻看不懂!! 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值