分数
T1:IP地址(ip) 100
T2:是否同构(same) 50
T3:箱子(box) 10
T4:社恐的聚会(party) 10
总分:170
题解
T1:IP地址(ip)
题意:
有个设备,每个设备都有它的名称和地址,有
次询问,每次询问输出给出的地址指向哪一个设备?
赛时总结:
一眼,5分钟速通。
题解:
拿map标记一遍,最后直接输出就行。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,q;
map<string,string> mp;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
string a,b;
cin>>a>>b;
mp[b]=a;
}
cin>>q;
for(int i=1;i<=q;i++){
string a;
cin>>a;
cout<<mp[a]<<'\n';
}
return 0;
}
T2:是否同构(same)
题意:
有两个长度为的数组
,我们想知道数组
和数组
是否是同构数组?
定义两个数组同构,则存在一个整数
,使得
,保持数组
不改动,交换数组
的前
项和后
项交换位置后,使
,
赛时总结:
直接写了暴力,TLE卡了50分。
思路:
我们可以找到在
数组中出现的位置(记为
),然后整体交换
与
,再判断
和
是否相同即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int T,n,a[1000005],b[1000005],t[1000005];
bool fun(){
for(int i=1;i<=n;i++){
if(a[i]!=b[i]){
return 0;
}
}
return 1;
}
int main(){
scanf("%d",&T);
while(T--){
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);
}
if(fun()){
printf("Yes\n");
continue;
}
int pos=(n>>1)+1;
for(;pos<=n;pos++){
if(a[pos]==b[1]) break;
}
for(int i=pos;i<=n;i++){
swap(a[i],a[i-pos+1]);
}
if(fun()) printf("Yes\n");
else printf("No\n");
}
return 0;
}
T3:箱子(box)
题意:
有个箱子,每个箱子的重量为
,每次可以将至多
个箱子合并成一个重量为这几个箱子重量和的箱子,花费的体力是这几个箱子的重量和。求出将所有箱子合并成一个箱子所花费的最少体力。
赛时总结:
第一眼:合并果子 10分后:? 讲完后:完了理解错题了。
思路:
可以想到,每次合并最小的箱子是最优的,但是不一定每次合并都一定正好有个箱子。
所以
1.当,在这种情况时,我们每次取
个合并最终可以刚好合并成一个,每次取最小的 个合并即可。
2.当,在这种情况时,一定会发生一次当前剩余数量不够
个箱子合并的情况,可以补足若干个
,使得其满足
,再按照1的办法进行合并。
AC代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,sum;
priority_queue<ll,vector<ll>,greater<ll> > pq;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int x;
cin>>x;
pq.push(x);
}
while((pq.size()-1)%(m-1)) pq.push(0);
while(pq.size()>1){
ll cnt=0;
for(int i=1;i<=m;i++){
if(!pq.size()) break;
cnt+=pq.top();
pq.pop();
}
sum+=cnt;
pq.push(cnt);
}
cout<<sum;
return 0;
}
T4:社恐的聚会(party)
题意:
有个患有社交恐惧症的人参与一个聚会,但聚会只有两张桌子,这些社恐们不想跟自己不认识的人坐在一起,能不能将这
个人分在两张桌子,使得每张桌子的任意两个人都是相互认识的。
赛时总结:
直接输出No得了十分,题是一点没看懂。
思路:
我们把所有不互相认识的人之间连一条无向边,然后可以对于所有连通块使用黑白染色法判断当前连通块是否是二分图,如果不是二分图,则直接输出 NO ,否则可以记录一下每个连通块中黑点的数量和白点的数量(代表在第一张桌子还是第二张桌子)。然后求得每个连通块的黑点和白点数量后,可以做一遍背包dp来求解答案。
设表示前
个连通块,是否能塞入
个点到第一张桌子(白点)。
设表示前
个连通块,是否能塞入
个点到第二张桌子(黑点)。
每个连通块的黑点和白点是可以互换的
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=514;
int n,m,cnt,a[N][N],sz[N][2],col[N];
bool vis[N],f[N];
bool dfs(int u,int c){
vis[u]=1,col[u]=c,sz[cnt][c]++;
for(int i=1;i<=n;i++){
if(i!=u&&(!a[i][u]||!a[u][i])){
if(vis[i]){
if(col[u]==col[i]) return false;
}
else if(!dfs(i,1-c)) return false;
}
}
return true;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=n;i++){
if(!vis[i]){
cnt++;
if(!dfs(i,0)){
printf("No\n");
return 0;
}
}
}
m=n>>1;
f[0]=1;
for(int i=1;i<=cnt;i++){
for(int j=m;;j--){
if(j<sz[i][0]&&j<sz[i][1]) break;
if(j>=sz[i][0]) f[j]|=f[j-sz[i][0]];
if(j>=sz[i][1]) f[j]|=f[j-sz[i][1]];
}
}
for(int j=m;j>=1;j--){
if(f[j]){
printf("Yes\n%d",n-j);
break;
}
}
return 0;
}
总结
第三题还是不够细心,但至少还有10分,第四题如果加个特判还能骗30分,希望下次能沉下心来,早日AKJ组。