KDY-3-ZHX补题报告

1.得分

1.IP地址2.是否同构3.箱子4.社恐的聚会总分
100pts10pts0pts10pts120pts

2.赛中概况

第一题跟前两天差不多,10分钟左右敲完代码,AC。

第二题用的是暴力枚举,预期拿60分,实际10分,但把j写成了1,只得10分.

第三题正解是优先队列,没接触过,笔记

第四题正解用的是dp+二分图,更没学过,骗分10分。

3.题目分析

T1 IP地址

赛中情况 AC

【题目描述】

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

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

【输入格式】

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

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

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

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

【输出格式】

对于 QQ 次询问,每次询问输出该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%30% 数据,1≤N,M≤1001≤N,M≤100;

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

                                                      华丽分割线                                                              

分析

直接暴力即可O(n^2) 

AC码

#include<bits/stdc++.h>
using namespace std;
const int N=100500;
struct qq{   //结构体
	string nu;  //用户名
	string ip;  //IP
}a[N];
int main(){
	//freopen("ip.in","r",stdin);
	//freopen("ip.out","w",stdout);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){   //输入
		cin>>a[i].nu>>a[i].ip;
	}int m;
	cin>>m;
	for(int i=1;i<=m;i++){  //询问
		string x;
		cin>>x;
		for(int j=1;j<=n;j++){  //遍历查询
			if(a[j].ip==x){
				cout<<a[j].nu<<endl;
				break;
			}
		}
	}
	return 0;
}

T2 

【题目描述】

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

【输入格式】

第一行输入一个正整数 T ,表示有 T 组输入;
每组输入的第一行,输入一个正整数 NN ;
第二行输入 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≤106;1≤ai,bi≤N,且有 ai≠aj(i≠j)a​i​​≠a​j​​(i≠j)。

                                                      华丽分割线                                                              

合析

sub1


根据题目已知k 的范围,然后可以暴力枚举k ,然后进行交换,过程中如果能得到与 数组相同的结果,则是 Yes 。
注意,在这个过程中,需要保留原数组,方便改变k 后,继续以原数组进行交换。


sub2


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

AC码:

#include<bits/stdc++.h>
using namespace std;
int a[1000005],b[1000005];
int main(){
	//freopen("same.in","r",stdin);
	//freopen("same.out","w",stdout);
	int t,n;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=0;i<n;i++)scanf("%d",&a[i]);
		for(int i=0;i<n;i++)scanf("%d",&b[i]);
		if(n==1&&a[0]!=b[0]){
			printf("No\n");     //特判
			continue;
		}else if(n==1){
			printf("Yes\n");
			continue;
		}
		int lf=0,xf=-1,lb=0,xb=(n+1)/2,tmp=0;
           //定义数组
		for(int i=0;i<n;i++){              //重构
			if(a[i]==b[i]&&!tmp||!tmp&&i>=(n+1)/2)tmp=true;
			if(a[i]!=b[i]&&!tmp){
				lf++;
				xf=i;
			}else if(a[i]!=b[i]){
				if(lb==0)xb=i;
				lb++; 
			}
		}
		tmp=false;
		for(int i=0,j=xb;i<=xf&&j<n;i++,j++){     //是否相同
			if(a[i]!=b[j]){
				tmp=true;
				break;
			}
		}//cout<<lf<<' '<<lb<<' '<<xf<<' '<<xb<<' '<<tmp<<endl;
		if(tmp||lf!=lb)printf("No\n");
		else printf("Yes\n");
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

 T3

【题目描述】

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

【输入格式】

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

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

【输出格式】

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

【输入样例 1】
 
 
  1. 3 2
  2. 1 2 3
【输出样例 1】
 
 
  1. 9
【输入样例 2】
 
 
  1. 7 3
  2. 1 2 3 4 5 6 7
【输出样例 2】
 
 
  1. 49
【输入样例 3】
 
 
  1. 3 3
  2. 1 2 3
【输出样例 3】
 
 
  1. 6

【数据范围】

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

对于 100%100% 的数据,2≤M≤N≤1052≤M≤N≤10​5​​,每个箱子的重量 1≤wi≤1061≤w​i​​≤10​6​​。

                                                      华丽分割线                                                              

分析 :

sub1


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


sub2


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

AC码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
priority_queue<ll,vector<ll>,greater<ll> > q;

int main(){
	
	int n,m;
	cin>>n>>m;
	int x;
	for(int i=1;i<=n;i++){
		cin>>x;
		q.push(x);
	}ll ans=0;
	if((n-m)%(m-1)!=0){
		ll 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){
		ll sum=0;
		for(int i=1;i<=m;i++){
			sum+=q.top();
			q.pop();
		}q.push(sum);
		ans+=sum;
	}cout<<ans;
	return 0;
}
/*
3
3
1 2 3
3 2 1
4
3 1 2 4
4 3 1 2
5
2 3 1 4 5
5 3 1 4 2
*/

T4

【题目描述】

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

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

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

【输入格式】

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

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

【输出格式】

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

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

【输入样例 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】

 
 
  1. 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%30% 的数据,1≤N≤201≤N≤20;

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

                                                      华丽分割线                                                                

 分析 

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

AC码

#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、付费专栏及课程。

余额充值