团队设计天梯赛L2题解集
前言
在刷L2的过程中,深感基础之薄弱,特写此博客总结。
L2-001 紧急救援
题意:求最短路径的条数,在最短的路径中找出一条结点合最大的,并且输出路径。
思路: dijkstra算法,sum[i]是存的当前节点到根节点最小路径数目,num[i]存的当前结点到根节点的最大节点和。每次松弛操作如果d[v]>d[x]+w 就更新最小路径数目 sum[v]=sum[x]; nums[v]+=o[x];并记录路径. 如果d[v]==d[x]+w就说明找到了另一条最小路径故sum[v]+=sum[x],再判断结点合是不是最大的if(num[v]<num[x]+o[v])
不是的话就更新。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=505;
const int maxm=250000+5;
const int INF=0x3f3f3f3f;
struct E
{
int to,w,next;
} edge[maxm];
int head[maxn],vis[maxn],d[maxn],sum[maxn],num[maxn],path[maxn],o[maxn];
int n,m,S,D;
int tot=1;
void AddEdge(int u,int v,int w)
{
edge[tot].w=w;
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
priority_queue<pair<int,int> > q;
void dijikstra()
{
for(int i=0; i<=n; i++) d[i]=INF,vis[i]=0;
d[S]=0;
sum[S]=1;
q.push(make_pair(0,S));
while(q.size())
{
int x=q.top().second;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=head[x]; i; i=edge[i].next)
{
int v=edge[i].to,w=edge[i].w;
if(d[v]>d[x]+w)
{
d[v]=d[x]+w;
sum[v]=sum[x];
num[v]=num[x]+o[v];
path[v]=x;
q.push(make_pair(-d[v],v)); //填入负数,因为优先队列默认排大的数
}
else if(d[v]==d[x]+w){
sum[v]+=sum[x];
if(num[v]<num[x]+o[v]){
num[v]=num[x]+o[v];
path[v]=x;
}
}
}
int c[maxn];
void print(){
int pre=path[D];
int tot=0;
while(pre!=-1){
c[tot++]=pre;
pre=path[pre];
}
while(tot--){
cout<<c[tot]<<" ";
}
}
}
cout<<D;
}
int main()
{
cin>>n>>m>>S>>D;
path[S]=-1;
for(int i=0;i<n;i++) cin>>num[i],o[i]=num[i];
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
AddEdge(u,v,w);
AddEdge(v,u,w);
}
dijikstra();
printf("%d %d\n",sum[D],num[D]);
print();
return 0;
}
L2-002 链表去重
思路: 看了网上一个大佬的思路,给结点定义一个num 用来表示链表时链表从头到尾的编号,当遇到重复的键值结点时,便让编号+n,这样就可省去对地址删改的操作。
然后按num大小排序,即可按链表的顺序输出。
view code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e7+5;
const int INF=0x3f3f3f3f;
struct node{
int address,key,next,num;
}a[maxn];
bool cmp(node a,node b){
return a.num<b.num;
}
bool vis[maxn];
int head,n;
int main()
{
cin>>head>>n;
for(int i=0;i<maxn;i++) a[i].num=INF;
for(int i=0;i<n;i++){
int x;cin>>x;
cin>>a[x].key>>a[x].next;
a[x].address=x;
}
int num1=0,num2=0;
for(int i=head;i!=-1;i=a[i].next){
if( !vis[abs(a[i].key)] ){
vis[abs(a[i].key)]=true;
a[i].num=num1++;
}
else{
a[i].num=1+n+num2; num2++;
}
}
sort(a,a+maxn,cmp);
int num3=num1+num2;
for(int i=0;i<num3;i++){
if(i!=num1-1&&i!=num3-1){
printf("%05d %d %05d\n",a[i].address,a[i].key,a[i+1].address);
}else{
printf("%05d %d -1\n",a[i].address,a[i].key);
}
}
return 0;
}
L2-003 月饼
思路:模拟,按价值高的排序,然后判断库存是否大于需求
view code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
struct node{
double p,r;
}a[maxn];
bool cmp(node a,node b){
return a.p>b.p;
}
int n,w;
int main()
{
cin>>n>>w;
for(int i=0;i<n;i++) cin>>a[i].r;
for(int i=0;i<n;i++){
double x;cin>>x;
a[i].p=x/a[i].r*1.00;
}
sort(a,a+n,cmp);
double ans=0;
for(int i=0;i<n;i++){
if(w>=a[i].r){
w-=a[i].r;
ans+=a[i].r*a[i].p;
}
else{
ans+=w*a[i].p;
w=0;
}
if(w==0) break;
}
printf("%.2lf",ans);
return 0;
}
L2-004 这是二叉搜索树吗?
思路: 被这个题目搞的头疼,自己写的程序有个测试点一直爆栈(递归用的太多),然后叕叕看了大佬的写法。
具体思路不难想,要判断两次(非镜像和镜像),然后分情况建树,输出。难点在于优化,判断和建树程序的优化。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
int a[maxn];
int n;
struct node{
int val;
node *left;
node *right;
node(int v,node *l,node *r){
val=v; left=l;right=r;
}
};
node *root=NULL;
int isBST(int i,int j){
if(i>=j) return 1;
int p=j;
for(int k=i+1;k<=j;k++){
if(a[k]>=a[i]){
p=k;break;
}
}
for(int z=p+1;z<=j;z++){
if(a[z]<a[i]) return 0;
}
return isBST(i+1,p-1)&&isBST(p,j);
}
int isBST2(int i, int j){
if(i>=j) return 1;
int p=j;
for(int k=i+1;k<=j;k++){
if(a[k]<a[i]){
p=k;break;
}
}
for(int z=p+1;z<=j;z++){
if(a[z]>=a[i]) return 0;
}
return isBST2(i+1,p-1)&&isBST2(p,j);
}
//普通递归建树,有个测试点会栈溢出
node *build1(int i,int j){
if(i==j) return new node(a[i],NULL,NULL);
if(i>j) return NULL;
int p=i+1;
while(a[p]<a[i]) p++;
return new node(a[i], build1(i+1,p-1), build1(p,j));
}
//镜像
node *build2(int i,int j){
if(i==j) return new node(a[i],NULL,NULL);
if(i>j) return NULL;
int p=i+1;
while(a[p]>=a[i]) p++;
return new node(a[i], build2(i+1,p-1), build2(p,j));
}
// 优化的建树方法
node *build3(node *t,int key){
if(t==NULL){ return new node(key,NULL,NULL);}
if(key>=t->val) t->right=build3(t->right,key);
else t->left=build3(t->left,key);
return t;
}
// 镜像
node *build4(node *t,int key){
if(t==NULL){ return new node(key,NULL,NULL);}
if(key<t->val) t->right=build4(t->right,key);
else t->left=build4(t->left,key);
return t;
}
static int sum=0;
void print(node *root){
if(root==NULL) return;
print(root->left);
print(root->right);
if(sum++!=0) printf(" ");
printf("%d",root->val);
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
if(isBST(0,n-1)){
printf("YES\n");
for(int i=0;i<n;i++)
root=build3(root,a[i]);
print(root);
}
else if(isBST2(0,n-1)){
printf("YES\n");
for(int i=0;i<n;i++)
root=build4(root,a[i]);
print(root);
}
else{
printf("NO\n");
}
return 0;
}
L2-005 集合相似度
思路:两个集合交集元素的个数/(两个集合元素的个数-交集元素个数) ,set存放即可
view code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int INF=0x3f3f3f3f;
set<int> s[maxn];
int n,k;
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
int m; cin>>m;
while(m--){
int x; cin>>x;
s[i].insert(x);
}
}
cin>>k;
while(k--){
int a,b;
cin>>a>>b;
int num=0;
for(int x:s[a]){
if(s[b].count(x)) ++num;
}
double ans= 1.00*num/(s[a].size()+s[b].size()-num);
printf("%.2lf%\n",ans*100);
}
return 0;
}
L2-006 树的遍历
题意:给定一棵树的后序和中序,输出层序遍历。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=35;
struct node{
int left,right;
}a[maxn];
int m[maxn],b[maxn];
int build(int ml,int mr, int bl, int br){
if(ml>mr){
return -1;
}
int root=b[br],index=ml,num=0;
while(m[index]!= b[br])index++;
num=index-ml;
a[root].left=build(ml,index-1,bl,bl+num-1);
a[root].right=build(index+1,mr,bl+num,br-1);
return root;
}
queue<int> q;
int flag=0;
void bfs(int root){
q.push(root);
while(q.size()>0){
int len=q.size();
while(len--){
int x=q.front(); q.pop();
if(flag++==0) cout<<x;
else cout<<" "<<x;
if(a[x].left!=-1) q.push(a[x].left);
if(a[x].right!=-1) q.push(a[x].right);
}
}
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>b[i];
for(int i=0;i<n;i++) cin>>m[i];
build(0,n-1,0,n-1);
int root=b[n-1];
bfs(root);
return 0;
}
L2-007 家庭房产
思路:并查集,不会。叕学习了下大佬的代码。题意要求以家庭中编号最小的成员代表这个家庭,故在并查集merge操作中,每次合并让编号小的作父节点,并更新值。
用set存放出现过的人,保证遍历找每个家庭代表时不会重复。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int f[maxn],pnum[maxn];
int n;
set<int> s;
int id,fa,mo,k,child;
double fn,fc,fnum[maxn],fcover[maxn];
//fn:房产数量,fc:房产面积 fnum[] 存放每个结点的房产数量,fcover[]每个结点的房产数量
void init(){
for(int i=0;i<10005;i++){
f[i]=i;
pnum[i]=1;//家庭人口数初始为1
}
}
int found(int x){
return x==f[x]?x:f[x]=found(f[x]);
}
int merg(int x,int y){
int fx=found(x),fy=found(y);
if(fx!=fy){
if(fx<fy) swap(fx,fy);
pnum[fy]+=pnum[fx];
fnum[fy]+=fnum[fx];
fcover[fy]+=fcover[fx];
f[fx]=fy;
}
return fy;
}
bool cmp(int a,int b){
if(fcover[a]==fcover[b]) return a<b;
return fcover[a]>fcover[b];
}
vector<int> ans;
int main()
{
init();
cin>>n;
for(int i=0;i<n;i++){
cin>>id>>fa>>mo>>k;
s.insert(id);
if(fa!=-1){
s.insert(fa);
id=merg(id,fa);
}
if(mo!=-1){
s.insert(mo);
id=merg(id,mo);
}
while(k--){
cin >> child;
s.insert(child);
id = merg(id,child);
}
cin >> fn >> fc;
fnum[id] += fn;
fcover[id] += fc;
}
for(set<int>::iterator it=s.begin(); it!=s.end(); it++){
if(f[*it]==*it){
fnum[*it]/=pnum[*it];
fcover[*it] /= pnum[*it];
ans.push_back(*it);
}
}
sort(ans.begin(),ans.end(),cmp);
cout<<ans.size()<<endl;
for(vector<int>::iterator it = ans.begin();it != ans.end();it ++) {
printf("%04d %d %.3f %.3f\n",*it,pnum[*it],fnum[*it],fcover[*it]);
}
return 0;
}
L2-008 最长对称子串
思路: 一开始以为是dp,结果没搞出来。看了题解暴力遍历即可.
view code
#include <iostream>
using namespace std;
int main() {
string s;
getline(cin, s);
int maxvalue = 0, temp;
int len = s.length();
for(int i = 0; i < len; i++) {
temp = 1;
for(int j = 1; j < len; j++) {
if(i - j < 0 || i + j >= len || s[i - j] != s[i + j])
break;
temp += 2;
}
maxvalue = temp > maxvalue ? temp : maxvalue;
temp = 0;
for(int j = 1; j < len; j++) {
if(i - j + 1 < 0 || i + j >= len || s[i - j + 1] != s[i + j])
break;
temp += 2;
}
maxvalue = temp > maxvalue ? temp : maxvalue;
}
cout << maxvalue;
return 0;
}
L2-009 抢红包
思路: 模拟排序即可
view code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e4+5;
struct node{
int id;
int hb;
double num;
}a[maxn];
bool cmp(node a,node b){
if(a.num==b.num){
if(a.hb==b.hb) return a.id<b.id;
return a.hb>b.hb;
}
return a.num>b.num;
}
int main()
{
int N;
scanf("%d",&N);
for(int i=0;i<N;i++){ a[i].num=0.00,a[i].id=i; }
for(int i=0;i<N;i++){
int k;
scanf("%d",&k);
while(k--){
int x;
scanf("%d",&x);
int y; scanf("%d",&y);
a[x-1].num+=y;
a[x-1].hb++;
a[i].num-=y;
}
}
sort(a,a+N,cmp);
for(int i=0;i<N;i++){
printf("%d %.2lf\n",a[i].id+1,a[i].num/100);
}
return 0;
}
L2-010 排座位
思路: 二维数组建图存关系,可能存在 A是B朋友,B是C朋友,C是D朋友,所以A和D是朋友这种情况,所以用并查集判断两人是否是朋友。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e2+5;
const int inf=0x3f3f3f3f;
int edge[105][105];
int f[105];
int found(int x){
if(x==f[x]){
return x;
}
return f[x]=found(f[x]);
}
void uion(int a,int b){
int t1=found(a),t2=found(b);
if(t1!=t2) f[t1]=t2;
}
int main()
{
int n,m,k;
cin>>n>>m>>k;
memset(edge,inf,sizeof(edge));
for(int i=1;i<=n;i++) f[i]=i;
while(m--){
int u,v,w; cin>>u>>v>>w;
if(w==1)
uion(u,v);
edge[u][v]=w;
edge[v][u]=w;
}
while(k--){
int x,y;
cin>>x>>y;
if(edge[x][y]==inf) cout<<"OK\n";
else{
int flag=0;
if(found(x)==found(y)&&edge[x][y]==-1) {cout<<"OK but...\n";flag=1;}
if(!flag){
if(edge[x][y]==1) cout<<"No problem\n";
else cout<<"No way\n";
}
}
}
return 0;
}
L2-011 玩转二叉树
题意: 给定先序和中序建立二叉树,然后层序遍历的时候,每层倒着输出。和第六题中序,后序建树差不多。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=35;
struct Node{
int left,right;
}node[maxn];
int n;
int a[maxn];
int b[maxn];
int build(int la,int ra,int lb,int rb){
if(lb>rb) return 0;
if(lb==rb) return b[lb];
int root=b[lb];
int index=la;
for(;index<=ra;index++){
if(a[index]==root)break;
}
int num=index-la;
node[root].left=build(la,index-1,lb+1,lb+num); //注意这里不是num-1了
node[root].right=build(index+1,ra,lb+num+1,rb);
return root;
}
queue<int> q;
stack<int> ans;
int flag=0;
void bfs(int root){
if(root!=0)
q.push(root);
while(q.size()>0){
int len=q.size();
while(len--){
int x=q.front(); q.pop();
if(node[x].left!=0)q.push(node[x].left);
if(node[x].right!=0) q.push(node[x].right);
ans.push(x);
}
while(ans.size()!=0) {
if(flag!=0)
cout<<" "<<ans.top();
else
cout<<ans.top();
ans.pop();
flag++;
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
for(int i=0;i<n;i++)cin>>b[i];
int root=build(0,n-1,0,n-1);
bfs(root);
return 0;
}
L2-012 关于堆的判断
思路:小根堆,完全二叉树且子节点不得小于父节点。 因为是完全二叉树我们从编号1开始计数, 编号为i的结点子节点为2i和2i+1,父节点为i/2;
建树:每次添加结点,要判断是否大于父节点,如果不是,就交换子父结点,循环地判断直到父节点为根节点时。
查询:在一维数组里遍历树,返回结点下标。注意:因为层数可能不同,两个下标中较小的那个如果为奇数则一定不是兄弟结点。
view code
#include <iostream>
using namespace std;
const int maxn=1e3+5;
int h[maxn];
int n,m;
void update(int i){
if(i==1) return;
while(i>1){
if(h[i]<h[i/2]){
swap(h[i],h[i/2]);
i/=2;
}
else return;
}
}
void findp(int a,int b,int &ap,int &bp){
for(int i=1;i<=n;i++){
if(h[i]==a) ap=i;
else if(h[i]==b) bp=i;
}
}
int main()
{
string s;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>h[i];update(i);
}
for(int i = 0;i<m;++i){
int a,b;
int ap,bp;
cin>>a;
cin>>s;
if(s=="and"){///兄弟
cin >> b;
getline(cin,s);
findp(a,b,ap,bp);
if(ap>bp) swap(ap,bp);
if(ap%2==0 && bp-ap == 1 ) cout<<"T"<<endl;
else cout<<"F"<<endl;
}
else{
cin>>s;
if(s=="a"){///a 是 b 孩子
cin>>s;cin>>s>>b;
findp(a,b,ap,bp);
if(ap/2 == bp) cout<<"T"<<endl;
else cout<<"F"<<endl;
}
else{
cin>>s;
if(s=="root"){///根
if(h[1]==a) cout<<"T"<<endl;
else cout<<"F"<<endl;
}
else{///a 是 b父亲
cin>>s>>b;
findp(a,b,ap,bp);
if(bp/2 == ap) cout<<"T"<<endl;
else cout<<"F"<<endl;
}
}
}
}
return 0;
}
L2-013 红色警报
太菜了,一开始竟想到图论去了。叕学习了大佬们的博客
题意:找连通块,去掉一个结点后如果图中连通块的数量减少则发出警报
思路:两次dfs寻找连通块,找连通块:对每个结点都判断一下是否访问过,以及是否存在。
先dfs找不去除一个结点的连通块数,随后p[x]=1;代表去除结点,再找去除结点后的连通块数。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=505;
const int maxm=5005;
const int inf=0x3f3f3f;
int n,m;
struct E{
int to,next;
}edge[maxm*2];
int head[505],vis[505],p[505];
int tot=1;
void AddEdge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs(int x){
vis[x]=1;
for(int i=head[x]; i!=0; i=edge[i].next){
int v=edge[i].to;
if(vis[v]||p[v]) continue;
vis[v]=1;
dfs(v);
}
}
int main()
{
scanf("%d %d",&n,&m);
while(m--){
int u,v;
scanf("%d %d",&u,&v);
AddEdge(u,v); AddEdge(v,u);
}
int k;scanf("%d",&k);
int sum=0;
while(k--){
memset(vis,0,sizeof(vis));
int x;scanf("%d",&x);
sum++;
int cnt1=0,cnt2=0;
for(int i=0;i<n;i++){
if(!vis[i] && !p[i] ){
cnt1++;dfs(i);
}
}
p[x]=1;
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++){
if(!vis[i] && !p[i]){
cnt2++; dfs(i);
}
}
if(cnt2-cnt1>0)
printf("Red Alert: City %d is lost!\n",x);
else
printf( "City %d is lost.\n",x);
if(sum==n) printf("Game Over.");
}
return 0;
}
L2-014 列车调度
思路: 求最大能划分的下降序列的个数 == 求最长上升子序列的长度
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int dp[maxn];
int pos[maxn];
int main()
{
int n; scanf("%d ",&n);
int len=0;
int ans=0;
while(n--){
int a; scanf("%d",&a);
if(len==0||a>dp[len]) dp[++len]=a;
else{
int l=upper_bound(dp+1,dp+len+1,a)-dp;
dp[l]=a;
}
}
cout<<len;
return 0;
}
L2-015 互评成绩
思路: 模拟即可,用了优先队列弹出成绩前M高的数。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
priority_queue<double> q;
double ans[maxn];
int main()
{
int k,m,n;
scanf("%d %d %d",&k,&m,&n);
for(int i=0;i<k;i++){
double mmax=-1.00,mmin=500.00;
double sum=0;
for(int j=0;j<m;j++){
double x;scanf("%lf",&x);
mmax=max(x,mmax);
mmin=min(x,mmin);
sum+=x;
}
sum-=mmax+mmin;
double num=1.000*sum/(m-2);
q.push(num);
}
int c=0;
while(n--){
ans[c++]=q.top();
q.pop();
}
for(int i=c-1;i>=0;i--){
if(i==0)
printf("%.3lf",ans[i]);
else
printf("%.3lf ",ans[i]);
}
return 0;
}
L2-016 愿天下有情人都是失散多年的兄妹
思路: 两次搜索,第一次把五代以内的结点全部标记一遍,第二次如果访问到了就说明不能结婚
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
map<int,int> f,m,vis,s;
int n;
int id,fa,ma;
string sex;
bool check(int a,int b){
vis.clear();
queue<int> q;
q.push(a);
int height=1;
while(q.size()>0&&height<=5){
int len=q.size();
while(len--){
int x=q.front();q.pop();
if(f[x]!=0) q.push(f[x]);
if(m[x]!=0) q.push(m[x]);
vis[x]=1;
}
height++;
}
q.push(b);
height=1;
while(q.size()>0&&height<=5){
int len=q.size();
while(len--){
int x=q.front();q.pop();
if(vis[x]==1) return false;
if(f[x]!=0) q.push(f[x]);
if(m[x]!=0) q.push(m[x]);
vis[x]=1;
}
height++;
}
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
while(n--){
cin>>id>>sex>>fa>>ma;
if(sex[0]=='F') s[id]=1;
else s[id]=2;
if(fa!=-1)s[fa]=2,f[id]=fa;
if(ma!=-1)s[ma]=1,m[id]=ma;
}
int k;cin>>k;
while(k--){
int a,b; cin>>a>>b;
if(s[a]==s[b]) cout<<"Never Mind\n";
else if(check(a,b)) cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}
L2-017 人以群分
思路:前缀和维护总活跃度,排序,n是偶数对半分,n是奇数,两种情况分别判断下哪个多就完事。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
long long sum[maxn];
int a[maxn];
int main()
{
int n;scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
sort(a,a+n);
sum[0]=a[0];
for(int i=1;i<n;i++){
sum[i]+=sum[i-1]+a[i];
}
int n1,n2;
long long ans=0;
if(n%2==0) { n1=n2=n/2; ans=sum[n-1]-2*sum[n/2-1];}
else{
if(sum[n-1]-2*sum[n/2-1]>=sum[n-1]-2*sum[n/2]){
n1=n/2;n2=n-n1;
ans=sum[n-1]-2*sum[n/2-1];
}
else{
n1=n/2+1;n2=n-n1;
ans=sum[n-1]-2*sum[n/2];
}
}
printf("Outgoing #: %d\n",n2);
printf("Introverted #: %d\n",n1);
printf("Diff = %lld\n",ans);
return 0;
}
L2-019 悄悄关注
思路:模拟即可
view code
#include <bits/stdc++.h>
#include <string>
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
unordered_set<string> s;
vector <string> ans;
string a[maxn];
int b[maxn];
int main()
{
ios::sync_with_stdio(false);
int n,m;
cin>>n;
for(int i=0;i<n;i++){
string t;cin>>t;
s.insert(t);
}
cin>>m;
double sum=0;
for(int i=0;i<m;i++){
cin>>a[i];
cin>>b[i];
sum+=b[i];
}
double ave=sum/=m;
for(int i=0;i<m;i++){
if(b[i]>ave&&s.find(a[i])==s.end()){
ans.push_back(a[i]);
}
}
sort(ans.begin(),ans.end());
for(string i:ans){
cout<<i<<endl;
}
if(ans.size()==0)
cout<<"Bing Mei You";
return 0;
}
L2-020 功夫传人
思路: 链式前向星存图,然后按照层序遍历的思路就ok了
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e5+5;
struct E{
int to,next;
}edge[maxn<<1];
int head[maxn],a[maxn];
int tot=0;
void AddEdge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
queue<int> q;
int main()
{
ios::sync_with_stdio(false);
memset(head,-1,sizeof(head));
int n; double z,r;
cin>>n>>z>>r;
for(int i=0;i<n;i++){
int k; cin>>k;
if(k==0) cin>>a[i];
else
while(k--){
int v;cin>>v;
AddEdge(i,v);
}
}
q.push(0);
double sum=0,cnt=1.00;
r*=0.01;
while(q.size()>0){
int saize=q.size();
while(saize-->0){
int x=q.front(); q.pop();
if(head[x]==-1){
sum+=a[x]*cnt;continue;
}
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].to;
q.push(v);
}
}
cnt*=(1-r);
}
int ans=(int)(sum*z);
cout<<ans;
return 0;
}
L2-021 点赞狂魔
思路: 模拟排序即可
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
struct node{
string name;
int len,ave;
}a[maxn];
bool cmp(node a,node b){
if(a.len==b.len){
return a.ave<b.ave;
}
return a.len>b.len;
}
int main()
{
ios::sync_with_stdio(false);
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].name;
int k;cin>>k;
set<int> s;
a[i].ave=k;
while(k--){
int x;cin>>x;
s.insert(x);
}
a[i].len=s.size();
}
sort(a,a+n,cmp);
if(n<3)
for(int i=0;i<3;i++){
if(i!=0) cout<<" ";
if(i<n)
cout<<a[i].name;
else cout<<"-";
}
else
for(int i=0;i<3;i++){
if(i!=0) cout<<" ";
cout<<a[i].name;
}
return 0;
}
L2-022 重排链表
思路有点混乱,对于这种链表的题目,一时间总是搞不清楚该怎样存链表,无奈又看了题解,真菜。
思路: 结构体存放输入数据,先用一个数组表示这条链表 s[0],s[1]依次代表链表的顺序,再定义一个数组,从开头和结尾向中间循环。
view code
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 1e5+50;
int s[maxn],s1[maxn];
struct P{
int x,y;
}a[maxn];
int main()
{
int add,d,n,xx,t,j=0;
scanf("%d %d",&xx,&t);
for(int i=0;i<t;i++)
{
scanf("%d %d %d",&add,&d,&n);
a[add].x = d;
a[add].y = n;
}
while(xx!=-1){
s[j++] = xx;
xx = a[xx].y;
}
int l=0,r=j-1,q=0;
while(l<=r){
if(l==r) s1[q++] = s[l++];
else{
s1[q++] = s[r--];
s1[q++] = s[l++];
}
}
for(int i=0;i<j-1;i++)
printf("%05d %d %05d\n",s1[i],a[s1[i]].x,s1[i+1]);
printf("%05d %d -1\n",s1[j-1],a[s1[j-1]].x);
return 0;
}
L2-023 图着色问题
思路: 链式前向星存边,判断每个结点,有个坑点是颜色一定要完全相等才算一个解。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=505;
const int maxm=250000+5;
struct E{
int to,next;
}edge[maxm];
int head[maxn],judge[maxn];
int n,m,k;
int tot=0;
void AddEdge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
set<int> s;
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m>>k;
while(m--){
int u,v;cin>>u>>v;
AddEdge(u,v); AddEdge(v,u);
}
int T; cin>>T;
while(T--){
s.clear();
for(int i=1;i<=n;i++){
cin>>judge[i];
s.insert(judge[i]);
}
int len=s.size();
if(len!=k){cout<<"No\n";continue;}
int flag=0;
for(int i=1;i<=n;i++){
for(int j=head[i];j!=0;j=edge[j].next){
int v=edge[j].to;
if(judge[i]==judge[v]){flag=1;break;}
}
if(flag) break;
}
if(!flag)cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}
L2-024 部落
思路:并查集
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int f[maxn];
int find(int x){
return x==f[x]? x:f[x]=find(f[x]);
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx!=fy){
f[fx]=fy;
}
}
set<int> s;
int main()
{
ios::sync_with_stdio(false);
int n; cin>>n;
for(int i=1;i<=10000;i++){
f[i]=i;
}
while(n--){
int m; cin>>m;
int pre;
for(int i=0;i<m;i++){
int x;cin>>x;s.insert(x);
if(i==0) {pre=x; continue;}
merge(pre,x);
}
}
int sum=0;
int slen=s.size();
for(int i=1;i<=slen;i++){
if(f[i]==i) sum++;
}
cout<<slen<<" "<<sum<<endl;
int T;cin>>T;
while(T--){
int a,b; cin>>a>>b;
if(find(a)==find(b)){
cout<<"Y\n";
}
else
cout<<"N\n";
}
return 0;
}
L2-025 分而治之
思路: dfs求连通块,和红色警报那题差不多
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e4+50;
struct E{
int to,next;
}edge[maxn];
int head[maxn],vis[maxn],p[maxn];
int tot=0;
void AddEdge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs(int x){
vis[x]=1;
for(int i=head[x];i!=0;i=edge[i].next){
int v=edge[i].to;
if(vis[v]||p[v]) continue;
vis[v]=1;
dfs(v);
}
}
int n,m,k;
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
while(m--){
int u,v; cin>>u>>v;
AddEdge(u,v); AddEdge(v,u);
}
cin>>k;
while(k--){
memset(vis,0,sizeof(vis));
memset(p,0,sizeof(p));
int np; cin>>np;
for(int i=0;i<np;i++){
int x; cin>>x;
p[x]=1;
}
int cnt=0;
for(int i=1;i<=n;i++){
if(!vis[i]&&!p[i]){
cnt++;dfs(i);
}
}
if(cnt==n-np)cout<<"YES\n";
else cout<<"NO\n";
}
return 0;
}
L2-026 小字辈
思路: 链式前向星存图,层序遍历记录最深层的结点
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
struct E{
int to,next;
}edge[maxn];
int head[maxn];
int tot=0;
void AddEdge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
queue<int> q;
vector<int> ans;
int main()
{
ios::sync_with_stdio(false);
memset(head,-1,sizeof(head));
int n;cin>>n;
int root=0;
for(int i=1;i<=n;i++){
int x;cin>>x;
if(x!=-1)
AddEdge(x,i);
else
root=i;
}
q.push(root);
int cnt=1;
while(q.size()>0){
int len=q.size();
ans.clear();
while(len--){
int x=q.front(); q.pop();
ans.push_back(x);
if(head[x]==-1)continue;
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].to;
q.push(v);
}
}
cnt++;
}
cnt--;
cout<<cnt<<"\n";
sort(ans.begin(),ans.end());
int l=ans.size();
for(int i=0;i<l;i++){
if(i!=0)cout<<" ";
cout<<ans[i];
}
return 0;
}
L2-027 名人堂与代金劵
思路: 模拟,排序
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
struct node{
string name;
int len;
}a[maxn];
int N,G,K;
int sum=0;
bool cmp(node a,node b){
if(a.len==b.len){ sum++; return a.name<b.name; }
return a.len>b.len;
}
int main()
{
ios::sync_with_stdio(false);
cin>>N>>G>>K;
int ans=0;
for(int i=0;i<N;i++){
cin>>a[i].name;
cin>>a[i].len;
if(a[i].len>=G) ans+=50;
else if(a[i].len>=60) ans+=20;
}
sort(a,a+N,cmp);
cout<<ans<<"\n";
int cnt=1;
int pre=-1;
for(int i=0;cnt<=K;i++){
if(a[i].len==pre){
cout<<cnt<<" "<<a[i].name<<" "<<a[i].len<<"\n";
}
else{
pre=a[i].len;cnt=i+1;
if(cnt>K) break;
cout<<cnt<<" "<<a[i].name<<" "<<a[i].len<<"\n";
}
}
return 0;
}
L2-028 秀恩爱分得快
思路: 一开始没有考虑编号为0的情况,直接用整型的正负代表男女,后来改成字符输入,然而还是有个测试点不过,
整吐了,爷不写了。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
vector<int> g[maxn];
double a[maxn],b[maxn];
vector<int> ansA,ansB;
int main()
{
ios::sync_with_stdio(false);
int f0=1;
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++){
int k; cin>>k;
while(k--){
string s;cin>>s;
int x=stoi(s);
if(s[0]=='-'&&x==0)f0=0;
g[i].push_back(x);
}
}
int A,B; cin>>A>>B;
if(A<0){
for(int i=0;i<m;i++){
int flag=0;
for(int j:g[i]){ if(j==A) flag=1;}
if(!flag)continue;
int len=g[i].size();
for(int j:g[i]){
if(j>0) a[j]+=1.00/len;
else if(j==0&&f0){ a[j]+=1.00/len;}
}
}
for(int i=0;i<m;i++){
int flag=0;
for(int j:g[i]){ if(j==B) flag=1;}
if(!flag)continue;
int len=g[i].size();
for(int j:g[i]){
if(j<0) b[-j]+=1.00/len;
else if (j==0&&!f0){b[j]+=1.00/len;}
}
}
double maxA=-1, maxB=-1;
for(int i=0;i<n;i++){
if(a[i]>maxA) maxA=a[i];
if(b[i]>maxB) maxB=b[i];
}
for(int i=0;i<n;i++){
if(a[i]==maxA)
ansA.push_back(i);
if(b[i]==maxB)
ansB.push_back(i);
}
for(int i:ansA){
if(i==B){
for(int j:ansB){
if(j==-A){
if(A==0&&!f0) cout<<"-"<<A<<" "<<B<<"\n";
else if(B==0&&!f0)cout<<A<<" -"<<B<<"\n";
else
cout<<A<<" "<<B<<"\n";
return 0; }
}
}
}
for(int i:ansA) {
cout<<A<<" "<<i<<"\n";
}
for(int j:ansB){
if(j==0&&!f0) cout<<B<<" -"<<-j<<"\n";
else
cout<<B<<" "<<-j<<"\n";
}
}
else{
//A>=0 B<0
for(int i=0;i<m;i++){
int flag=0;
for(int j:g[i]){ if(j==A) flag=1;}
if(!flag)continue;
int len=g[i].size();
for(int j:g[i]){
if(j<0) a[-j]+=1.00/len;
else if(j==0&&!f0) {a[-j]+=1.00/len;}
}
}
for(int i=0;i<m;i++){
int flag=0;
for(int j:g[i]){ if(j==B) flag=1;}
if(!flag)continue;
int len=g[i].size();
for(int j:g[i]){
if(j>0) b[j]+=1.00/len;
else if(j==0&&f0){b[j]+=1.00/len;}
}
}
double maxA=-1, maxB=-1;
for(int i=0;i<n;i++){
if(a[i]>maxA) maxA=a[i];
if(b[i]>maxB) maxB=b[i];
}
for(int i=0;i<n;i++){
if(a[i]==maxA)
ansA.push_back(i);
if(b[i]==maxB)
ansB.push_back(i);
}
for(int i:ansA){
if(i==-B){
for(int j:ansB){
if(j==A){
if(A==0&&!f0) cout<<"-"<<A<<" "<<B<<"\n";
else if(B==0&&!f0)cout<<A<<" -"<<B<<"\n";
else
cout<<A<<" "<<B<<"\n";
return 0; }
}
}
}
for(int i:ansA) {
if(i==0&&!f0) cout<<A<<" -"<<-i<<"\n";
else
cout<<A<<" "<<-i<<"\n";}
for(int j:ansB) cout<<B<<" "<<j<<"\n";
}
return 0;
}
L2-029 特立独行的幸福
思路:方法有点暴力,对于每一个数都判断能否变为1并将途中的数标记,若能变为1则has将原数标为1.若新变成的数已访问过,则退出循环。
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int A,B;
int vis[maxn],has[maxn];
int p[maxn];
int getint(int x){
int sum=0;
while(x>0){
int t=x%10;
x/=10;
sum+=t*t;
}
return sum;
}
bool is_prime(int x)
{
if(x<=1) return false;
for(int i=2;i*i<=x;i++)
if(x%i==0)
return false;
return true;
}
void judge(int x){
memset(vis,0,sizeof(vis));
int c=x;
int sum=x;
vis[x]=1;
while(sum!=1){
sum=getint(x);
p[c]++;
if(sum==1){ has[c]=1;break;}
if(vis[sum]==1){
has[c]=-1;break;
}
vis[sum]=1;
has[sum]=-1;
x=sum;
}
}
int main()
{
ios::sync_with_stdio(false);
cin>>A>>B;
for(int i=A;i<=B;i++){
if(!has[i])
judge(i);
}
int flag=0;
for(int i=A;i<=B;i++){
if(has[i]==1){
flag=1;
if(is_prime(i)) cout<<i<<" "<<2*p[i]<<endl;
else cout<<i<<" "<<p[i]<<endl;
}
}
if(flag==0)
cout<<"SAD"<<endl;
return 0;
}
L2-030 冰岛人
思路: 题目思路不难想,两个for循环遍历五代以内所有的结点,建图和处理字符串有点繁琐,学习了下大佬的写法,定义字符数组
要删去某个位置以后的字符,只需让当前位置的字符等于整数0,还有当输入时每次遇到一个新名字,就给它赋值一个编号。
view code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <unordered_map>
using namespace std;
char fname[21],lname[21],fname1[21],lname1[21];
int n,m,no,flag;
unordered_map<string,int> pos;
bool gender[200001];//1男0女
int f[200001];
bool check(int a,int b) {
int i=1,j=1;
for(int A=a; A!=0; A=f[A],i++){
j=1;
for(int B=b;B!=0;B=f[B],j++){
if(i>=5&&j>=5) break;
if(A==B&&(i<5||j<5))return false;
}
}
return true;
}
int main() {
scanf("%d",&n);memset(f,0,sizeof(0));
for(int i = 0;i < n;i ++) {
scanf("%s%s",fname,lname);
if(!pos[fname]) pos[fname] = ++ no;
int len = strlen(lname),d = pos[fname];
if(lname[len - 1] == 'n') {
gender[d] = 1;
lname[len - 4] = 0;
if(!pos[lname]) {
pos[lname] = ++ no;
gender[no] = 1;
}
f[d] = pos[lname];
}
else if(lname[len - 1] == 'r') {
gender[d] = 0;
lname[len - 7] = 0;
if(!pos[lname]) {
pos[lname] = ++ no;
gender[no] = 1;
}
f[d] = pos[lname];
}
else if(lname[len - 1] == 'm') gender[d] = 1;
else gender[d] = 0;
}
scanf("%d",&m);
for(int i = 0;i < m;i ++) {
scanf("%s%s%s%s",fname,lname,fname1,lname);
int a = pos[fname],b = pos[fname1];
if(!a || !b) printf("NA\n");
else if(gender[a] == gender[b]) printf("Whatever\n");
else printf("%s\n",check(a,b) ? "Yes" : "No");
}
}
L2-031 深入虎穴
思路:建图,输出离根节点最远的结点
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
struct E{
int to,next;
}edge[maxn<<1];
int head[maxn],vis[maxn];
int tot=0;
void AddEdge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
queue<int> q;
int main()
{
ios::sync_with_stdio(false);
memset(head,-1,sizeof(head));
int n; cin>>n;
for(int i=1;i<=n;i++){
int k;cin>>k;
while(k--){
int v;cin>>v;
vis[v]=1;
AddEdge(i,v);
}
}
int root=0;
for(int i=1;i<=n;i++){
if(!vis[i]) {root=i;break;}
}
q.push(root);
int ans=n;
while(q.size()>0){
int len=q.size();
while(len--){
int x=q.front();q.pop();
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].to;ans=v;
if(head[v]==-1) continue;
q.push(v);
}
}
}
cout<<ans;
return 0;
}
L2-032 彩虹瓶
思路: 栈的经典例题,火车进站
view code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int a[maxn],s[maxn];
int main()
{
ios::sync_with_stdio(false);
int n,m,k;
cin>>n>>m>>k;
while(k--)
{
for(int i=1; i<=n; i++) cin>>a[i];
int top=0;
int now=1;
int flag=0;
for(int i=1;i<=n;i++){
if(a[i]==now){
now++;
while(top&&s[top]==now){
top--;now++;
}
}
else{
s[++top]=a[i];
if(top>m){flag=1;}
}
}
if(!flag&&!top)cout<<"YES\n";
else cout<<"NO\n";
}
return 0;
}

浙公网安备 33010602011771号