6.7练习赛题解

C题下标打错错90分5555

A.移棋子游戏

问题描述

猫王和老板在玩一个游戏。桌面上一行有n个格子,一些格子中放着棋子。猫王和老板轮流选择如下方式中的一种移动棋子(图示中o表示棋子,*表示空着的格子):
1)当一枚棋子的右边是空格子的话,可以将这枚棋子像右移动一格。
o* -> *o
2)当一枚棋子的右边连续两个都有棋子,并且这个棋子往右边数第3格没有棋子,那么可以将这个棋子往右跳过那两个棋子
*ooo -> **oo
当任何一枚棋子到达最右边的格子时,这枚棋子自动消失。
当一方不能移动时,这方输。假设猫王和老板都采取最优策略,猫王先走,谁将取胜?

输入格式

第一行一个整数T表示数据组数, 0 < T < 10。
之后T组数据,每组两行,
第一行n 表示格子个数,
第二行n个字符表示每个格子的情况,o表示有棋子,*表示空着。

输出格式

对于每组数据一个输出,M表示猫王赢,L表示老板赢。

数据范围

对于50%的数据, n <20。
对于100%的数据, n < 1000。

题解:

显然n的范围不可能暴力搜索(况且还是签到题呢)所以考虑数学方法啦。。。
首先,不管是一格一格地动还是三格三格地动,它都只会跳奇数个格子,所以我们就将每个棋子要跳的距离累加起来,计算它的奇偶性即可。由于猫王先手,显然奇数次跳完棋子他剩;反之老板胜利。

代码:

#include<iostream>
#include<cstdio>
using namespace std;
int sum[1010];
char a[1010];
char read(){
    char c=getchar();
    while(c!='*'&&c!='o')c=getchar();
    return c;
}
int main(){
    int st=0,i,j,t,n,l=1,r=1,k;
    scanf("%d",&t);t++;
    while(--t){
        st=0;
        scanf("%d",&n);
        for(i=1;i<=n;i++)a[i]=read();
        for(i=1;i<=n;i++)if(a[i]=='o')st+=n-i;//累加棋子需要走的距离 
        if(st%2==1)printf("M\n");//奇数猫王赢 
        else printf("L\n");//否则老板赢 
    }
    return 0;
}

B.导航

问题描述

约翰在他的新车上装了两个导航系统(GPS),但这两个GPS选择的导航线路常常不同,约翰很是恼火。
约翰所在的小镇地图由N个路口和M条单向道路构成,两个路口间可能有多条道路相连。约翰的家在1号路口,他的农场在N号路口。约翰从家出发,可以经过一系列的道路,最终到达农场。
两个GPS用的都是上述地图,但是,它们计算时间的算法不同。比如,经过第i条道路,1号GPS计算出的时间是Pi分钟,而2号GPS算出的时间是Qi分钟。
约翰想要驾车从家到农场,但是,如果一个GPS认为约翰当前行走的这条路不在它算出的最短路径中,该GPS就会大声抱怨约翰走错了路(每个GPS会算出所有最短路,只要你在其中一条上最短路上它都不会抱怨)。更倒霉的是,有可能两个GPS会同时抱怨约翰当前走的路不是它们推荐的。
请帮助约翰计算,从家到农场过程中,选择怎样的路径才能使得GPS抱怨的次数最少,请算出这个最少的抱怨次数。如果一条路上两个GPS都在抱怨,算两次(+2)抱怨。

输入格式

第1行两个空格间隔的整数,N和M
接下来M行,每行描述一条道路。第i行描述第i条道路,由四个空格间隔的整数构成,Ai,Bi,Pi,Qi,分别表示该条道路的起点、终点、1号GPS计算的耗时、2号GPS计算的耗时。

输出格式

一个整数,表示所求答案。

数据范围

对于30%的数据,1<=N<=20,1<=M<=20
对于100%的数据,1<=N<=10000,1<=M<=50000,0<=Pi,Qi<=100000

题解:

最开始还以为是图上动归。。。。想多了。。。没想出正解直接暴零
首先数据范围就已经决定计算GPS是否会抱怨是需要预处理出来的。先计算对于每个GPS计算终点到每个点的最短路径,然后对于一条从x->y的边时,一定是dis[y]-dis[x]==len[x][y]时,这条边才在该点到终点的路径上的。最后再从起点到终点以GPS抱怨次数为边权跑一遍最短路即可。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int maxn=10000;
int n,m,dis1[maxn+10],dis2[maxn+10],dis3[maxn+10];
vector <pair<int,int> > ma1[maxn+10],ma2[maxn+10];
vector <pair<int,pair<int,int> > > ma3[maxn+10];
vector <pair<int,pair<int,int> > > ::iterator inli;
vector <pair<int,int> > ::iterator it;
priority_queue <pair<int,int> > heap;
void dij1(){
    int i,x,w;
    memset(dis1,3,sizeof(dis1));
    dis1[n]=0;heap.push(make_pair(0,n));
    while(!heap.empty()){
        x=heap.top().second;w=-heap.top().first;heap.pop();
        if(w!=dis1[x])continue;
        for(it=ma1[x].begin();it!=ma1[x].end();it++){
            if(dis1[x]+it->first<dis1[it->second]){
                dis1[it->second]=dis1[x]+it->first;
                heap.push(make_pair(-dis1[it->second],it->second));
            }
        }
    }
}
void dij2(){
    int i,x,w;
    memset(dis2,3,sizeof(dis2));
    dis2[n]=0;heap.push(make_pair(0,n));
    while(!heap.empty()){
        x=heap.top().second;w=-heap.top().first;heap.pop();
        if(w!=dis2[x])continue;
        for(it=ma2[x].begin();it!=ma2[x].end();it++){
            if(dis2[x]+it->first<dis2[it->second]){
                dis2[it->second]=dis2[x]+it->first;
                heap.push(make_pair(-dis2[it->second],it->second));
            }
        }
    }
}
void dij3(){
    int i,x,w,u;
    memset(dis3,3,sizeof(dis3));
    dis3[1]=0;heap.push(make_pair(0,1));
    while(!heap.empty()){
        x=heap.top().second;w=-heap.top().first;heap.pop();
        if(w!=dis3[x])continue;
        for(inli=ma3[x].begin();inli!=ma3[x].end();inli++){
            u=2;if(dis1[x]-dis1[inli->first]==inli->second.first)u--;//计算GPS是否会抱怨 
            if(dis2[x]-dis2[inli->first]==inli->second.second)u--;
            if(dis3[inli->first]>dis3[x]+u){
                dis3[inli->first]=dis3[x]+u;
                heap.push(make_pair(-dis3[inli->first],inli->first));
            }
        }
    }
}
int main(){
    int i,j,x,y,w1,w2;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d%d%d",&x,&y,&w1,&w2);
        ma1[y].push_back(make_pair(w1,x));
        ma2[y].push_back(make_pair(w2,x));
        ma3[x].push_back(make_pair(y,make_pair(w1,w2)));//存边 
    }
    dij1();//GPS1的最短路 
    dij2();//GPS2的最短路 
    dij3();//计算最小代价 
    printf("%d",dis3[n]);
    return 0;
}

3.小鸟

问题描述

有一排n棵树,第i棵树的高度是Di。
一群小鸟要从第1棵树飞到第n棵树去玩。
不同小鸟的飞跃能力不同,第i只小鸟的飞跃能力为ki,表示如果当前它位于第x号树,那么它可以飞到x+1,x+2,……,x+ki号树上去,也就是一次可以飞过ki棵树。
如果小鸟飞到一棵不矮于当前树的树,那么他的劳累值会+1,否则不会。
小鸟们希望最小化劳累值,请你计算每只小鸟达到终点所需最小劳累值。

输入格式

第一行,一个整数N(2<=N<=100000)
第二行,N个空格间隔的整数,第i个数表示第i棵树的高度Di。(1<=Di<=10^9)
第三行,一个整数Q(1<=Q<=25),表示小鸟的数量
接下来Q行,每行一个整数,其中第i个整数表示第i只小鸟的飞跃能力ki。
1<=Ki<=N-1

输出格式

Q行,每行一个整数,表示对应小鸟的劳累值

题解:

动归+优先队列优化。
首先状态是f[i]表示飞到i点需要的最少劳累值。
方程:枚举后k棵目标树j,若当前树比它高则f[j]=min(f[j],f[i]),否则f[j]=min(f[j],f[i]+1)
时间复杂度: O ( n k ) ,显然超时。
而n的范围几乎是满的,就可以确定该算法复杂度只能是 O ( n )
所以想到优先队列,维护一个单增的队列(注意不是树的高度是飞行的劳累值,劳累值要尽可能的小)
而每次新计算一棵树,只需判断它与队首高度差,若高于队首则f[j]=f[队首树]+1。
同时限制小鸟飞行距离。由于需要使队中树高尽可能高,若队尾 的f值等于f[i]且没有当前树高,则将其弹出。

代码:

#include<iostream>
#include<cstdio>
#include<deque>
#include<cstring>
using namespace std;
deque <pair<int,int> > q;
int f[100010],a[100010];
int main(){
    int i,j,n,Q,k;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    scanf("%d",&Q);
    for(j=1;j<=Q;j++){
        scanf("%d",&k);
        q.clear();
        memset(f,0,sizeof(f));//初始化 
        for(i=1;i<=n;i++){
            while(!q.empty()&&i-q.front().second>k)q.pop_front();//限制小鸟飞行距离
            if(q.empty())q.push_back(make_pair(a[i],i));//队为空则加入元素 
            else {
                if(q.front().first>a[i])f[i]=f[q.front().second];
                else f[i]=f[q.front().second]+1;//根据队首计算当前劳累值 
                while(!q.empty()&&(f[q.back().second]>f[i]||(f[q.back().second]==f[i]&&q.back().first<a[i])))q.pop_back();//更新队列 
                q.push_back(make_pair(a[i],i));
            }
        }
        printf("%d\n",f[n]);
    }
    return 0;
}

D.舞会

问题描述

约翰的N(2≤N≤10000)只奶牛非常兴奋,因为这是舞会之夜!她们穿上礼服和新鞋子,别上鲜花,她们要表演圆舞.
只有奶牛才能表演这种圆舞.圆舞需要一些绳索和一个圆形的水池.奶牛们围在池边站好,顺时针顺序由1到N编号.每只奶牛都面对水池,这样她就能看到其他的每一只奶牛.为了跳这种圆舞,她们找了M(2≤M≤50000)条绳索.若干只奶牛的蹄上握着绳索的一端,绳索沿顺时针方绕过水池,另一端则捆在另一些奶牛身上.这样,一些奶牛就可以牵引另一些奶牛.有的奶牛可能握有很多绳索,也有的奶牛可能一条绳索都没有。
对于一只奶牛,比如说贝茜,她的圆舞跳得是否成功,可以这样检验:沿着她牵引的绳索,按顺时针方向,找到她牵引的奶牛,再沿着这只奶牛牵引的绳索,又找到一只被牵引的奶牛,如此下去,若最终能回到贝茜,则她的圆舞跳得成功,如果这样的检验无法完成,那她的圆舞是不成功的.
如果两只成功跳圆舞的奶牛有绳索相连,那她们可以同属一个组.
给出每一条绳索的描述,请找出,成功跳了圆舞的奶牛有多少个组(要求一组至少有两头奶牛)?

输入格式

第1行输入N和M,
接下来M行每行两个整数A和B,表示沿顺时针方向A牵引着B.

输出格式

一个整数,表示成功跳圆舞的奶牛组数.

题解:

判环裸题。计算强联通分量,只要点超过一个ans++即可。

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int maxn=10000;
int sum,n,m,dfs[maxn+10],cnt=0;
bool mark[maxn+10];
vector <int> ma[maxn+10],nima[maxn+10];
void dfs1(int x){
    vector <int> ::iterator it;
    mark[x]=true;
    for(it=ma[x].begin();it!=ma[x].end();it++)if(!mark[*it])dfs1(*it);
    dfs[++cnt]=x;
}
void dfs2(int x){
    vector <int> ::iterator it;
    mark[x]=true;
    for(it=nima[x].begin();it!=nima[x].end();it++)if(!mark[*it])dfs2(*it);
    sum--;
}
int main(){
    int i,j,x,y,ans=0,pre;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        ma[x].push_back(y);
        nima[y].push_back(x);
    }
    for(i=1;i<=n;i++)
        if(!mark[i])dfs1(i);
    sum=n;memset(mark,0,sizeof(mark));
    for(i=n;i>=1;i--)if(!mark[dfs[i]]){
        pre=sum;
        dfs2(dfs[i]);
        if(pre-sum>1)ans++;//只要点个数超过1则ans++ 
    }
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42013837/article/details/80617035