目录
一.题目报告
赛中第一题AC,二题50,三四题10分。赛后一二三题AC。
二.赛中概况
第一题模拟秒了,第二题没有分析彻底,第三题使用优先队列,没有补零所以导致运行错误,
第四题骗了10分。
三.解题报告
T1.IP地址(ip)
题目情况
赛中AC。
题目大意
有 N 个设备,每个设备都有它的名称和IPv4地址,现在我们有 Q个问题,每次我们想知道给出的IPv4地址是哪一个设备
题目解析
数据范围不大,直接使用map或结构体模拟即可。
题目正解
#include<bits/stdc++.h>
using namespace std;
struct str{
string x,y;
}a[1001];
long long n,q;
string s;
int main(){
freopen("ip.in","r",stdin);
freopen("ip.out","w",stdout);
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].x>>a[i].y;
}
cin>>q;
while(q--){
cin>>s;
for(int i=0;i<n;i++){
if(s==a[i].y){
cout<<a[i].x<<endl;
break;
}
}
}
fclose(stdin);
fclose(stdout);
return 0;
}
T2.是否同构(same)
题目情况
赛中50分,赛后AC。
题目大意
两个数列a、b,求a的前k项与后k项交换后是否和b数列相同,即swap(a1,aN−k+1),⋯,swap(ak,aN))(0<=k<=n/2)。
题目解析
赛中思路:用双指针模拟k,分别将a数列从r到n,从l+1到r,从1到l与b数列比较。设置f=0作为判断,若比较完后f=0,输出Yes;否则l++,r--再进行比较,直到l>n/2。
观察到a和b都是一个排列,然后我们可以找到b[1]在a数组中出现的位置i(记为 pos),然后我们整体交换 a数列再判断和b是否相同即可。(记得要判断特殊情况:a=b)
题目正解
#include<iostream>
using namespace std;
long long t,n,a[1000001],b[1000001],f,pos;
bool check(){
for(int i=1;i<=n;i++){
if(a[i]!=b[i]){
return 0;
}
}
return 1;
}
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%lld",&b[i]);
}
if(check()){
printf("Yes\n");
}
for(int i=n/2+1;i<=n;i++){
if(a[i]==b[1]){
pos=i;
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;
}
T3.箱子(box)
题目情况
赛中10分,赛后AC。
题目大意
n个箱子,每次最多可以将m个箱子合并成一个重量为合并的几个箱子的总重量的箱子,耗费的体力为合并的几个箱子的总重量,求如何合并为一个箱子且耗体力最少。
题目解析
经过分析得出,每次合并重量最小的箱子最后耗费的体力最少,所以使用优先队列计算即可,但在n不为m倍数的时候要补零。
题目正解
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n,m,cnt,w;
priority_queue<long long,vector<long long>,greater<long long> > q;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>w;
q.push(w);
}
if((n-1)%(m-1)>0){
int sum=m-1-(n-1)%(m-1);
while(sum--){
q.push(0);
}
}
while(q.size()>1){
int u=0;
for(int i=1;i<=m;i++){
u+=q.top();
q.pop();
}
cnt+=u;
q.push(u);
}
cout<<cnt;
return 0;
}
T4.社恐的聚会(party)
题目情况
赛中10分。
题目大意
有n个社恐参加聚会,每个社恐不想和自己不认识的人坐在一张桌子,所以只有互相认识的社恐可以坐在一张桌子上。问是否能将他们安排在两张桌子上,如果能,再求能不能让这两张桌子中人数最多的一张桌子的入座人数最少。
题目解析
(这个是真超纲了,不会) 将互相不认识的人或只有一方认识的人之间连一条无向边,之后判断这些连通图是不是二分图(使用黑白染色法),不是输出No,是的话记录连通块中黑白点数量,记录完后做一遍dp求解答案。
设dp[i][j][0]表示前i个连通块,是否能塞入j个点到第一张桌子(白点)。设dp[i][j][1]表示前i个连通块,是否能塞入j个点到第二张桌子(黑点)。
注意每个连通块的黑点和白点是可以互换的,转移的时候需要注意
题目正解
还不是很理解这道题,就直接将题解放出来了
//二分图,黑白染色
//二分图:把所有的边一刀切,所有的点会变成孤立的点
#include<bits/stdc++.h>
using namespace std;
const int maxn=525;int head[maxn],nxt[maxn*maxn],to[maxn*maxn],cnt;
void add(int u,int v){
nxt[++cnt]=head[u];
to[cnt]=v;
head[u]=cnt;
}
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=head[u];i;i=nxt[i]){
int v=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(){
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]){
//=>!(g[i][j]&&g[j][i])
//=>只有相互认识的时候才不会产生连边,其他的情况都加边放图中
add(i,j);
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;
}
四.总结
考虑问题不全面,二题没有想到特殊情况,三题没有想到n不为m倍数的情况,中间还出现过思路中断的问题,还需多做题强化能力。