远古测试,我现在才写
T1 葡萄酒
话说题库里没有这道题?
考点:思维+二进制
有一点哈希的想法?
因为我们知道:每一个字符串进行转化后得到的结果总是不同的,自然,如果我们把一个01串看成一个字符串,其转换后所得的数值也是不同的
换言之:每一个二进制所对应的十进制数是唯一的
那么,我们就可以通过对小白鼠进行排列组合,所得到的若干二进制数,一一对应 n n n 个十进制数
考虑需要多少只小白鼠:
对于 1 1 1 瓶酒,我么们不需要小白鼠就知道它一定是有毒的,即需要 0 0 0 只小白鼠
对于 4 4 4 瓶酒,我们有如下的组合: 01 , 10 , 11 01,10,11 01,10,11 ,还有一瓶不喝,就需要 2 2 2 只小白鼠
对于 5 5 5 瓶酒,我们有如下的组合: 001 , 010 , 011 , 100 001,010,011,100 001,010,011,100 ,还有一瓶不喝,就需要 3 3 3 只小白鼠
得出规律:
因为有一瓶酒我们可以不试,所以,实际上只需要试 n − 1 n-1 n−1 瓶酒即可,而所需小白鼠的数量恰为 n − 1 n-1 n−1 转化为二进制数后的数位
过于简单,没有代码
这你要是写不出来可以回家写作业了
T2 雾山五行
考点:并查集
话说这不是模板吗?
反正用并查集维护就对了(
回去要好好感谢出题人
那么,在此题中,我们的并查集所维护的是该集合的战斗力,每一次合并所合并的也是两个集合的战斗力
最后依次枚举每一个集合,将其战斗力进行一个 max \max max 的比较即可
#include<cstdio>
#include<algorithm>
using namespace std;
int fa[5005],Fight[5005]; //Fight 数组存储战斗力
int a[5005];
void make(int num){
for(int i=1;i<=num;i++){
fa[i]=i;
}
}
int find(int num){
if(fa[num]==num){
return num;
}
return fa[num]=find(fa[num]);
}
void build(int x,int y){
int xx=find(x),yy=find(y);
if(xx!=yy){
Fight[yy]+=Fight[xx]; //战斗力的累加
fa[xx]=yy;
}
} //以上基本都是模板
int main(){
freopen("war.in","r",stdin);
freopen("war.out","w",stdout);
int n,m,x,y,ans=-2147483647;
scanf("%d%d",&n,&m);
make(n);
for(int i=1;i<=n;i++){
scanf("%d",&Fight[i]);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
build(x,y); //合并
}
for(int i=1;i<=n;i++){
ans=max(ans,Fight[find(i)]); //依次比较
}
printf("%d",ans); //输出
return 0;
}
T3 龙珠
考点:带权并查集
此题与银河英雄传说高度类似
需要知道A龙珠现在所在的城市,A所在的城市有几颗龙珠,A转移到这个城市移动了多少次
前两问应该是很好求得的,分别用 find 函数和一个处理集合大小的 size 数组即可
难的是第三问
我们先来定义一个 move 数组,表示移动次数
初始化是不难的: m o v e [ i ] = 0 move[\ i\ ]=0 move[ i ]=0
那么,我们需要什么时候更新 m o v e [ i ] move[\ i\ ] move[ i ] 呢?
自然,我们可以想到:在合并集合时,我们就可以更新 m o v e [ f i n d ( x ) ] move[\ find(x)\ ] move[ find(x) ] 的值了
void build(int x,int y){
int xx=find(x),yy=find(y);
if(xx!=yy){
deap[yy]+=deap[xx]; //处理集合大小
move[xx]++; //使 find(x) 的移动步数自增
fa[xx]=yy; //更新父亲
}
}
但是,我们遇到了与银河英雄传说同样的问题:我们只更新了每一个集合的祖先元素,但是,我们没有更新每一个集合的子元素
怎么办呢?
简单来说:在每一次进行 find 操作时,我们不要急着将 f i n d ( f a [ n u m ] ) find(fa[\ num\ ]) find(fa[ num ]) 的值赋给 f a [ n u m ] fa[\ num\ ] fa[ num ] ,而是先让 m o v e [ n u m ] + = m o v e [ f a [ n u m ] ] move[\ num\ ]+=move[\ fa[\ num\ ]\ ] move[ num ]+=move[ fa[ num ] ] ,再赋值
很简单,因为 find 是一个递归操作,当处理到 n u m num num 时, f a [ n u m ] fa[\ num\ ] fa[ num ] 已经处理好了,又因为 n u m num num 和 f a [ n u m ] fa[\ num\ ] fa[ num ] 处于同一集合内,所以 f a [ n u m ] fa[\ num\ ] fa[ num ] 移动了多少次, n u m num num 也要移动那么多次,自然就需要在 find 函数里更新 m o v e [ n u m ] move[\ num\ ] move[ num ] 了
完整代码:
#include<cstdio>
int fa[10005],deap[10005],move[10005];
void make(int num){
for(int i=1;i<=num;i++){
fa[i]=i,deap[i]=1,move[i]=0;
} //初始化
}
int find(int num){
if(fa[num]==num){
return num;
}
int tot=find(fa[num]);
move[num]+=move[fa[num]]; //上文已提,对 move 数组的更新
return fa[num]=tot;
}
void build(int x,int y){
int xx=find(x),yy=find(y);
if(xx!=yy){
deap[yy]+=deap[xx];
move[xx]++;
fa[xx]=yy;
} //上文已提
}
int main(){
int T;
scanf("%d",&T);
for(int TT=1;TT<=T;TT++){
printf("Case %d:\n",TT);
int n,m,x,y;
char ch;
scanf("%d%d",&n,&m);
make(n); //多组输入,每一次都要重新初始化
for(int i=1;i<=m;i++){
scanf("\n%c",&ch);
if(ch=='T'){ //合并操作
scanf("%d%d",&x,&y);
build(x,y);
}else{ //查询操作
scanf("%d",&x);
int tot=find(x);
printf("%d %d %d\n",tot,deap[tot],move[x]); //三个问题,依次得解
}
}
}
return 0;
}
T4 教练
考点:并查集
自然,在输入的时候,我们就可以对每一个团队进行初始化
若某同学没有任何要求,那么他跟其他任何一个人同一个团队都可以
自然,在进行初始化后,可能会出现只有一个人或两个人的团队
这时,我们需要将一个人的团队塞进两个人的团队里面去,如果没有两个人的团队了,就把所有一个人的团队凑到一起
所有团队合并好后,我们需要判断是否有解
假设有解,则人数为 3 3 3 的团队数必然是 n ÷ 3 n\div3 n÷3 (题目保证 n % 3 = 0 n\%3=0 n%3=0)
我们只需要统计人数为 3 3 3 的团队数量即可
无解,输出-1
有解,依次输出每一个团队里的成员即可
具体细节参考代码:
#include<cstdio>
int fa[105];
struct node{
int x[10],cnt; //x 表示具体成员, cnt 表示成员数
}b[105];
bool flag[105];
void make(int num){
for(int i=1;i<=num;i++){
fa[i]=i;
}
}
int find(int num){
if(fa[num]==num){
return num;
}
return fa[num]=find(fa[num]);
}
void build(int x,int y){
int xx=find(x),yy=find(y);
if(xx!=yy){
fa[xx]=yy;
}
} //并查集模板
int main(){
freopen("coach.in","r",stdin);
freopen("coach.out","w",stdout);
int n,m,x,y;
scanf("%d%d",&n,&m);
make(n);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
flag[x]=flag[y]=1; //标记,表明 i 和 j 不属于一人团队
build(x,y);
}
for(int i=1;i<=n;i++){
int tot=find(i); //找到 i 的祖先
b[tot].x[b[tot].cnt++]=i; //相应团队的成员数自增,并将 i 塞入该团队
}
for(int i=1;i<=n;i++){
if(flag[i]==1){ //不属于一人团队
continue;
}
bool tot=0;
for(int j=1;j<=n;j++){ //优先寻找2人团队
if(i==j){
continue;
}else if(b[j].cnt==2){ //找到了2人团队
b[j].x[b[j].cnt++]=i; //新增成员
b[i].cnt=b[i].x[0]=0; //原先 i 所在的团队数据清除
flag[i]=1;
flag[j]=1; //进行相应的标记
tot=1;
break;
}
}
if(tot){ //已经找到了2人团队
continue;
}
for(int j=1;j<=n;j++){ //找一人团队
if(i==j){
continue;
}else if(b[j].cnt==1){
b[j].x[b[j].cnt++]=i;
b[i].cnt=b[i].x[0]=0;
flag[i]=flag[j]=1;
break;
} //与寻找2人团队类似
}
}
int sum=0;
for(int i=1;i<=n;i++){
if(b[i].cnt==3){ //统计三人团队个数
sum++;
}
}
if(sum!=n/3){ //无解情况
printf("-1");
return 0;
}
for(int i=1;i<=n;i++){
if(b[i].cnt==3){ //依次输出每一个三人团队
printf("%d %d %d\n",b[i].x[0],b[i].x[1],b[i].x[2]);
}
}
return 0;
}