比赛情况
题目 | IP地址(ip) | 是否同构(same) | 箱子(box) | 社恐的聚会(party) |
赛中得分 | 100 | 100 | 0 | 10 |
赛后得分 | 100 | 100 | 100 | 10(输出no骗10分) |
题目分析
T1:IP地址(ip)
【题目描述】
IP地址(Internet Protocol Address)是互联网上用于识别和定位设备的数字标识。它是一种由32位或128位二进制数字组成的地址,在IPv4和IPv6两个主要版本中使用。
IP地址的主要功能是标识和寻址设备,使其能够在互联网上进行通信。通过将IP地址分配给计算机、服务器、路由器和其他网络设备,数据包可以被正确地发送到目标设备。IP地址还用于确定网络中不同设备的位置,以便进行网络管理和故障排除。
总之,IP地址是互联网上用于标识和定位设备的数字标识,使设备能够在互联网上进行通信。IP地址的外观根据其版本而有所不同。以下是IPv4和IPv6两个主要版本的IP地址中IPv4地址示例:
192.168.0.1 172.16.254.1 10.0.0.1 208.75.57.100
接下来,我们有 N 个设备,每个设备都有它的名称和IPv4地址,现在我们有 Q 个问题,每次我们想知道给出的IPv4地址是哪一个设备?
【输入格式】
第一行,一个正整数 N ,表示有 N 个设备;
接下去 N 行,首先输入该设备的名称,数据保证该设备的名称只由英文大小写组成,其次再输入该设备的IPv4地址;
接下去一行,输入一个正整数 Q ,表示有 Q 次询问;
接下去 Q 行,每行一个IPv4地址。
【输出格式】
对于 Q 次询问,每次询问输出该IPv4地址对应的设备名称。
【输入样例 1】
4 Main 192.168.0.1 Google 8.8.8.8 some 123.13.34.45 other 23.32.45.54 3 192.168.0.1 23.32.45.54 8.8.8.8
【输出样例1】
Main other Google
【数据范围】
对于 30% 数据,1≤N,M≤100;
对于 100% 数据,1≤N,M≤1000,设备的名称是只由大小写字母组成的长度小于等于100的字符串,IPv4地址”a.b.c.d”满足 0≤a,b,c,d≤255 。
题目大意
给定一个字符串和其对应的字符串,后给定第二个字符串,求第一个字符串
题目分析
刚开始看长题给我看晕了,还以为给一堆Ip让你把他们黑了(doge),后来一看这意思,手打个map,GAMEOVER!
正解代码
#include<bits/stdc++.h>
using namespace std;
map<string,string>ma;
int n,q;
string name,s;
int main(){
//freopen("ip.in","r",stdin);
//freopen("ip.out","w",stdout);
scanf("%d",&n);
for(int i=0;i<n;i++){
cin>>name>>s;
ma[s]=name;
}scanf("%d",&q);
for(int i=0;i<q;i++){
cin>>s;
cout<<ma[s]<<'\n';
}
//fclose(stdin);
//fclose(stdout);
return 0;
}/*
4
Main 192.168.0.1
Google 8.8.8.8
some 123.13.34.45
other 23.32.45.54
3
192.168.0.1
23.32.45.54
8.8.8.8
*/
T2:是否同构(same)
【题目描述】
有两个长度为 N 的数组 a,b,我们想知道数组 a 和数组 b 是否是同构数组?
我们定义两个数组 a,b 同构,则存在一个整数 k,使得 0≤k≤⌊N/2⌋ ,有保持数组 bb 不动的时候,交换数组 aa 的前 k 项和后 k 项交换位置,即 swap(a1,aN−k+1,⋯,swap(ak,aN)),使得新的数组 a 完全相等于数组 b。【输入格式】
第一行输入一个正整数 T ,表示有 T 组输入;
每组输入的第一行,输入一个正整数 N ;
第二行输入 N 个整数,表示数组 a;
第三行输入 N 个整数,表示数组 b。【输出格式】
如果数组 a 和数组 b 同构,则输出
Yes
,否则,输出No
。【输入样例 1】
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
【输出样例 1】
Yes No Yes
【数据范围与约定】
对于 30% 的数据有,1≤N≤10。
对于 100% 的数据有,1≤T≤10;1≤N≤10^6,;1≤ai,bi≤N,且有 ai≠aj(i≠j)。
题目大意
给定两个同样长度序列a和b,求他们是不是同构数组
同构数组:
指存在一个整数k(0≤k≤n/2)交换a0~ak与an-k+1~an的位置,是的a完全等于b
题目分析
赛中想的就是找到他的左边要交换的序列和右边要交换的序列,看看能不能求出来(实际上调来调去变得有些令人难以理解)
正解代码
#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;
//lf存储左边与b不同的连续长度,xf存储终点
//lb存储右边与b不同的连续长度,xb存储起点
for(int i=0;i<n;i++){
if(a[i]==b[i]&&!tmp||!tmp&&i>=(n+1)/2)tmp=true;
//如果出现了相同的并且此时仍旧在累加lf,就停止累加lf
//如果已经到达了一半仍旧在累加lf(即前一半全部不同) 也停止
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;
}
}//枚举查找的两个序列,判断是否相同
if(tmp||lf!=lb)printf("No\n");
else printf("Yes\n");
}
//fclose(stdin);
//fclose(stdout);
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
*/
T3:箱子(box)
【题目描述】
我们有 N 个箱子,每个箱子有自己的重量 wi,每次我们可以将至多 M 个箱子合并成一个重量为这几个箱子重量和的箱子,花费的体力是这合并的几个箱子的重量和。请问我们将这所有的箱子合并成一个箱子所需要花费的最少体力是多少?
【输入格式】
第一行,两个正整数 N ,M ,表示有 N 个箱子,每次操作至多可以合并 M 个箱子。
第二行,N 个正整数,表示这 N 个箱子每个箱子的重量。
【输出格式】
输出一个正整数,表示我们最少需要花费的体力是多少。
【输入样例 1】
3 2 1 2 3
【输出样例 1】
9
【输入样例 2】
7 3 1 2 3 4 5 6 7
【输出样例 2】
49
【输入样例 3】
3 3 1 2 3
【输出样例 3】
6
【数据范围】
对于 30% 的数据,2≤M≤N≤10^3。
对于 100% 的数据,2≤M≤N≤10^5,每个箱子的重量 1≤wi≤10^6。
题目大意
就是可以合并多个的石子合并
题目分析
第一眼——升级前缀和吗?
第二眼——这个数据……
所以手打一个优先队列版前缀和了
#include<bits/stdc++.h>
using namespace std;
int n,m;
priority_queue<int,vector<int>,greater<int> >q;
int main(){
//freopen("box.in","r",stdin);
//freopen("box.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
int a;
scanf("%d",&a);
q.push(a);
}int ans=0;
while(n>1){
if(n>=m){
int sum=0;
for(int i=0;i<m;i++){
sum+=q.top();
q.pop();
}q.push(sum);
ans+=sum;
n-=(m-1);
}else{
int sum=0;
for(int i=0;i<n;i++){
sum+=q.top();
q.pop();
}q.push(sum);
ans+=sum;
break;
}
}printf("%d",ans);
//fclose(stdin);
//fclose(stdout);
return 0;
}/*
3 2
1 2 3
7 3
1 2 3 4 5 6 7
3 3
1 2 3
*/
长,啰嗦,还爆零……
正解思路
因为为了使它的最终总量更少,我们应该使每次尽可能都合m,但是比如:6 3,如果每次合成都吃满m的话最后还会剩两个,我们就有两个选择:
第一个,直接合成,但是会花费更多的体力
第二个,在第一次合成的时候少合一个当然就行了
好了,上代码
正解代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
ll ans=0;
priority_queue<ll,vector<ll>,greater<ll> >q;
int main(){
//freopen("box.in","r",stdin);
//freopen("box.out","w",stdout);
scanf("%d%d",&n,&m);
int t=(n-m)%(m-1);
for(int i=0;i<n;i++){
int a;
scanf("%d",&a);
q.push(a);
}if(t!=0){
ll sum=0;
for(int i=1;i<=t+1;i++){//合并t+1一个元素,才是先删除t个元素
sum+=q.top();
q.pop();
}q.push(sum);
ans+=sum;
}
while(q.size()>=m){
ll sum=0;
for(int j=0;j<m;j++){
sum+=q.top();
q.pop();
}ans+=sum;
q.push(sum);
}cout<<ans;
//fclose(stdin);
//fclose(stdout);
return 0;
}/*
3 2
1 2 3
7 3
1 2 3 4 5 6 7
3 3
1 2 3
*/
T4:社恐的聚会(party)
【题目描述】
有 N 个患有社交恐惧症的人想参与一个聚会,但是这个聚会只有两张桌子,这些社恐们不想跟自己不认识的人坐在一起!
你是这次聚会的主办方,请你想想办法,看看能不能将这 N 个人分在两张桌子,使得每张桌子的任意两个人都是相互认识的。
如果你有办法让这 N 个人分在两张桌子,请你再想想办法看看能不能让这两张桌子中人数最多的一张桌子的入座人数最少呢?
【输入格式】
第一行输入一个整数 N ,表示有 N 个社恐。
接下去第 2 行至第 N+1 行,每行 N 个为 0 或者 1 的整数,表示第 i−1 号人是否认识第 j 号人,如果为 0 ,表示第 i−1 号社恐不认识第 j 号社恐,否则为 1 ,表示第 i−1 号社恐认识第 j 号社恐。(可以是第 i 号社恐认识第 j 号社恐,但是第 j 号社恐不认识第 i 号社恐)
【输出格式】
如果不能分成两张桌子坐下这 N 个人,请输出
No
;否则,先输出
Yes
,然后再输出一个正整数,表示将这 N 个人安排入座之后的两张桌子中人数最多的那张入座人数最少是多少。【输入样例 1】
3 0 1 1 1 0 1 0 1 0
【输出样例 1】
Yes 2
【输入样例 2】
3 0 0 0 0 0 0 0 0 0
【输出样例 2】
No
【输入样例 3】
4 0 1 1 1 0 0 1 1 0 1 0 1 1 0 0 0
【输出样例3】
Yes 2
【数据范围】
对于 30% 的数据,1≤N≤20;
对于 100% 的数据,1≤N≤512, a[i][i]=0,其中 a[i][i] 表示第i号社恐对自己的是否认识为“不认识”。
老师说这题超模了,说这道题要用二分图+dp做,所以给我们普及了一下二分图
在一张图中,如果能够把全部的点分到 两个集合 中,保证两个集合内部没有 任何边 ,图中的边 只存在于两个集合之间,这张图就是二分图
染色法(判断一个图是否为二分图):
算法原理就是,用 黑 与 白 这两种颜色对图中点染色(相当于给点归属一个集合),一个点显然不能同时具有两种颜色
OK就这样了下课
补:
第四题开题解了,在这里写上正解
我们把所有不互相认识的人之间连一条无向边,然后我们可以对于所有连通块判断当前连通块是否是二分图(使用黑白染色法),如果不是二分图,则直接输出 NO ,否则我们可以记录一下每个连通块中黑点的数量和白点的数量(代表在第一张桌子还是第二张桌子)。注意,黑点和白点本质没有任何区别,我们每个连通块都可以黑白互换。然后我们求得每个连通块的黑点和白点数量后,我们可以做一遍判定性背包dp来求解答案。
具体的,设dpij0表示前i个连通块,是否能塞入j个点到第一张桌子(白点)。设dpij1表示前i个连通块,是否能塞入j个点到第二张桌子(黑点)。
注意每个连通块的黑点和白点是可以互换的,转移的时候需要注意
#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;
}