看我博客的人越来越多力(喜
一、题目报告
第一题AC,第三题10pts。比赛后AK
二、比赛概况
10min前:死机了一台,不过还没编多少问题不大
花5minAC了第一道,看到了第三题,看出了贪心,利用优先队列去做
第四题一看不会,去做第二题,磕了1h30min磕出来半成品(事后发现错得离谱)
在3h的时候电脑又死机了,代码全无,最后老师给的时间把代码想着搓了一遍。预估200,实际110(第三题做法假了)
三、解题报告
T1 IP地址(ip)
得分情况
比赛时AC。
题目大意
给定n个网址,每个网址对应一个IP地址,给定m个网址(都已出现过),问该IP地址对应哪一个网址。
解题思路
一个网址对应一个IP地址,很容易想到映射容器。
正解代码
#include<bits/stdc++.h>
using namespace std;
map<string,string>mp;
int main(){
int n,m;
cin>>n;
string s,s1;
for(int i=1;i<=n;i++){
cin>>s>>s1;
mp[s1]=s;
}
cin>>m;
while(m--){
cin>>s;
cout<<mp[s]<<endl;
}
return 0;
}
T2 是否同构(same)
得分情况
比赛时0pts。
错因是没有想到思路,使用了复杂的队列。
题目大意
给定两个长度为 n 的数组 a ,b ,若存在正整数 ,使 a 数组前 k 个元素与后 k 个元素交换后全部等于 b 数组,则称a,b数组为同构数组。问a,b是否是同构数组。保证数组内无重复元素
解题思路
我们遍历一遍a数组,找到其中与b[1]相同的元素,标记位置。从标记位置到末端依次与前面元素交换,最后判断是否相等。
需要特判数组原本就相等的情况。
正解代码
#include<bits/stdc++.h>
using namespace std;
int a[114514],b[114514],n,m,t;
bool check(){
for(int i=1;i<=n;i++){
if(a[i]!=b[i])return 0;
}
return 1;
}
int main(){
cin>>t;
while(t--){
cin>>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=0;
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");
continue;
}
else printf("No\n");
}
return 0;
}
T3 箱子(box)
得分情况
比赛时10pts。
错因是没有特判,使用了另一种问题的思路。
题目大意
石子合并,但是一次可以合并m堆,求最小代价
解题思路
本题使用贪心算法。利用小根优先队列把较轻的石子浮在上面,依次合并。需要注意的是,如果可能出现剩余堆数少于m的情况,要进行特判(没有特判损失90pts)
正解代码
#include<bits/stdc++.h>
using namespace std;
priority_queue<long long,vector<long long>,greater<long long> >q;//小根堆
long long ans,n,m,a[114514];
int main() {
cin>>n>>m;
for(int i=1; i<=n; i++) {
cin>>a[i];
q.push(a[i]);
}
if((n-1)%(m-1)>0) {
int cnt=m-1-(n-1)%(m-1);//特判
while(cnt--) {
q.push(0);
}
}
while(q.size()>1) {
long long dm=0;
for(int i=1; i<=m; i++) {
dm+=q.top();
q.pop();
}
ans+=dm;
q.push(dm);
}
cout<<ans;
return 0;
}
T4 社恐的聚会(party)
得分情况
比赛时0pts。
错因是没有掌握相关知识。
题目大意
有 n 个社恐参加聚会,只有两张桌子,每个社恐希望与自己相互认识的人坐在一张桌上。给定社恐们的认识关系,问能否使每个社恐满足需求,如果可以,那么人数多的那张桌子坐的人要尽可能少。输出是(那么还要输出就座人数最多的桌子的人数)或否。
(认识关系可能不是相互的。i认识j,但j不一定认识i,有向图)
解题思路
把所有不互相认识的人之间连一条无向边,然后可以对于所有连通块判断当前连通块是否是二分图(使用黑白染色法),如果不是二分图,则直接输出No,否则可以记录一下每个连通块中黑点的数量和白点的数量(代表在第一张桌子还是第二张桌子)。注意,黑点和白点本质没有任何区别,每个连通块都可以黑白互换。然后求得每个连通块的黑点和白点数量后,可以做一遍判定性背包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) { //dfs图,判断是否是二分图
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() {
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
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;
}
四、总结
本场比赛中我出现了一些状况,我没有及时应变,错过了好多分数,在T3中还因细节错过90分。希望巩固学过算法,注重细节方面,学会及时调整自己。