目录
A 迷路的牛牛
题目描述
牛牛去犇犇老师家补课,出门的时候面向北方,但是现在他迷路了。虽然他手里有一张地图,但是他需要知道自己面向哪个方向,请你帮帮他。
输入
每个输入包含一个测试用例。
每个测试用例的第一行包含一个正整数,表示转方向的次数N(N<=1000)。
接下来的一行包含一个长度为N的字符串,由L和R组成,L表示向左转,R表示向右转。
输出
输出牛牛最后面向的方向,N表示北,S表示南,E表示东,W表示西。
样例输入 Copy
3
LRR
样例输出 Copy
E
分析:我直接说思路啦,首先记录L的个数和 R 的个数。
然后草稿纸上分析,对4取余。(四个方向 )W,S,E,N。
我们让L的个数减去 R 的个数,如果大于 0,我们只要全部向左转。
否则,我们就全部向右转。
if 是左转, 余数 为1时, 输出 W
余数 为 2 时, 输出S
余数 为 3 时, 输出 E
余数 为 0 时, 输出 N
if 是右转 , 余数 为1时, 输出 E
余数 为 2 时, 输出S
余数 为 3 时, 输出 W
余数 为 0 时, 输出 N
就是这样敲代码就好了。
代码实现:c语言
#include <stdio.h>
#include <stdlib.h>
int main (){
char a[2000];
int n;
while(~scanf("%d",&n)){
scanf("%s",a);
solve(a,n);
}
return 0;
}
void solve (char a[],int n){
int lc=0;
int rc=0;
for(int i=0;i<n;i++)
if(a[i]=='L')
lc++;
else rc++;
int count= lc-rc;
if(count>0) {
if(count%4==0) printf("N\n");
if(count%4==1) printf("W\n");
if(count%4==2) printf("S\n");
if(count%4==3) printf("E\n");
}
else {
count =0-count;
if(count%4==0) printf("N\n");
if(count%4==1) printf("E\n");
if(count%4==2) printf("S\n");
if(count%4==3) printf("W\n");
}
}
B 工作单位
样例输入 Copy
10 4
2 3
4 5
4 8
5 8
样例输出 Copy
7
分析:先假设一个人在一个单位上班,只要两个人在同一家单位上班,我们就 减一
以样例输入为例,2 和 3 在一个单位,10-1;
4 和 5 在一个单位 10-1-1;
4 和 8 在一个单位 10-1-1-1;
最后的 5 和 8 在一个单位,可以根据 前面两个推出来,所以不需要再 -1了。
然后为什么可以用并交集来解呢?
其实它刚好符合这种模型。 刚开始 2 和 3 的根节点 都是 它们本身 2,3 刚好也就符合 我们假设 的 2 和 3 在不同单位上班。
既然不在一个 单位上班,现在我们有需要在一个单位上班 ,于是 我们就 把 2 和 3 合并 起来,对应代码的话,2的根节点就是 3。
依此类推,4和 5 ,合并之后 , 4的根节点就是 5
4和8,合并之后,8的根节点也是 5
5和8 ,合并之后没反应,因为,他们的根节点都是 5.
题目思路就是这样,具体不懂的这个 并交集等上课内容,还是多看看老师的ppt。
代码实现:c语言
#include <stdio.h>
#include <stdlib.h>
int parents[200];
int rank[200];
int n,m;
int count;
int main (){
while(~scanf("%d %d",&n,&m)){
int x;
int y;
count=n;
init();
for(int i=0;i<m;i++)
{
scanf("%d %d",&x,&y);
union_set(x,y);
}
printf("%d",count);
printf("\n");
}
return 0;
}
void init(){
for(int i=1;i<=n;i++)
{
parents[i]=i;
rank[i]=0;
}
}
int findparents(int x){
if(parents[x]!=x)
parents[x]=findparents(parents[x]);
return parents[x];
}
void union_set(int x,int y){
x=findparents(x);
y=findparents(y);
if(x==y) return;
count--;
if(rank[x]>rank[y]){
parents[y]=x;
}
else{
parents[x]=y;
if(rank[x]==rank[y])
rank[y]++;
}
}
C 隔离14天
样例输入 Copy
100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
样例输出 Copy
4
分析:此题要用到并交集,我就不讲那个上课内容啦,很多很多看老师ppt理解最好。
我就讲讲我的解题思路。
先 写初始化函数, init(),让 所有人的 根节点是他本身,所有的 rank=0;
再 写找根节点函数 ,一定要是根节点。findparents();
再 写结合函数,两个根节点不一样的树 结合在一起。
那怎么找出 是 和 0在一起的人呢?
很简单,我们只要找出 和 0一样的根节点即可,为什么呢:
因为 有人 和 0有关联, 那么,这个人要么是 0的 父节点,要么就是 0的 子节点,很显然 ,他们的根节点是一样的。
那如果出现这种间接的情况呢? 比如:0 1
0 2
9 2。
我们如何 判定呢 9 肯定也要 隔离14 天呢?
很简单,还是上面说的,只要 9的根节点和 0 的根节点相同就是了。
比如 :(假如我都以后面的数 为准,不管rank了 )
0和1 结合, 根节点就是 1;
再 0和 2结合, 0之前的根节点是 1,2的根节点是它本身,所以结合之后 ,0,1,2的根节点全是 2;
再 9 和 2 结合 , 9之前的根节点是它本身,2的是2,所以 结合后,0,1,2,9的 根节点都是 2。
很明显,无论什么情况都能解了。
细节的地方,在于那个 读数的时候怎么读,很简单,我们就用一个循环即可,因为前面已经给出 可以循环的次数了。
然后分别把后面的数值 与 第一次循环 读入的数相结合即可。是一样的。
代码实现:c语言
#include <stdio.h>
#include <stdlib.h>
#define ll long long
int parents[200];
int rank[200];
int main (){
int n;
int m;
while(~scanf("%d %d",&n,&m)){
init(n);
for(int i=0;i<m;i++)
{
int number;
int x;
int temp;
scanf("%d %d",&number,&x);
for(int i=1;i<number;i++)
{
scanf("%d",&temp);
union_set(x,temp);
}
}
int count=0;
for(int i=0;i<n;i++)
if(parents[i]==parents[0])
count++;
printf("%d",count);
printf("\n");
}
return 0;
}
void init (int n){
for(int i=0;i<n;i++){
parents[i]=i;
rank[i]=0;
}
}
int findparents(int x){
if(parents[x]!=x)
parents[x]=findparents(parents[x]);
return parents[x];
}
void union_set(int x,int y){
x=findparents(x);
y=findparents(y);
if(x==y) return;
else{
if(rank[x]>rank[y])
parents[y]=x;
else{
parents[x]=y;
if(rank[x]==rank[y])
rank[y]++;
}
}
}
D 最小生成树(kruskal)
题目描述
编程实现Kruskal算法,求图的最小生成树(MST)的权重。
输入
每组数据分为两个部分,第一部分为图的点数n,和边数m,
第二部分为m行,每一行输入三个数字,前两个为两个顶点的编号,第三个为边权重。
输出
最小生成树的权重。
样例输入 Copy
3 3
0 1 10
0 2 15
1 2 50
样例输出 Copy
25
分析:这个题我就不多说了,上课多听老师讲,不要去作过多的笔记,网上都会有代码的,比如关注我
嘿嘿嘿。要用到 并交集。为什么要用到并交集呢?
因为它可以帮助我们检索是否已经用到 某两点了。
说到这,可能还有人不是很懂 ,在于kruskal,
kruskal就是把从小到大选边,只要这两条边够小,且其中关联的点,没有构成环路,即可。
n个点,我们也就只要选n-1条边即可。
所以综上我们什么要用到并交集, 因为结合后,他们的根节点总是相等,而我们可以
以此判断是否 构成回路。
代码实现:c语言
#include <stdio.h>
#include <stdlib.h>
struct shu{
int d1;
int d2;
int bian;
};
struct shu s[200];
int cmp(const void *a,const *b){
struct shu *c=(struct shu *)a;
struct shu *d=(struct shu *)b;
return c->bian-d->bian;
}
int parents[200];
int rank[200];
int sum =0;
int main (){
int n;
int m;
while(~scanf("%d %d",&n,&m)){
init(n);
for(int i=0;i<m;i++)
scanf("%d %d %d",&s[i].d1,&s[i].d2,&s[i].bian);
qsort(s,m,sizeof(s[0]),cmp);
for(int i=0;i<m;i++)
union_set(s[i].d1,s[i].d2,s[i].bian);
printf("%d",sum);
printf("\n");
sum=0;
}
return 0;
}
void init (int n){
for(int i=0;i<n;i++)
{
parents[i]=i;
rank[i]=0;
}
}
int findparents(int x){
if(parents[x]!=x)
parents[x]=findparents(parents[x]);
return parents[x];
}
void union_set(int x,int y,int bian){
x=findparents(x);
y=findparents(y);
if(x==y) return;
else{
sum+=bian;
if(rank[x]>rank[y])
parents[y]=x;
else{
parents[x]=y;
if(rank[x]==rank[y])
rank[y]++;
}
}
}
E 搭建电路
题目描述
明明迷上了一个搭建电路的游戏。
在游戏中,每次在两个电子元件之间增加一条有效电路(两个元件之间先前没有电路相连)都将获得相应的积分奖励。
已知电子元件数量n和部分电子元件之间的奖励积分值。如何构建一个有效电路将所有元件全部连接起来,并且可以得到最多的积分奖励。
输入
每组输入数据包含m+1行。
第1行输入两个正整数n和m,其中n表示电子元件数量(n<=100),m表示提供了m对电子元件之间的奖励积分值(m<=1000)。两个正整数之间用空格隔开。
第2行到第m+1行对应m对电子元件及其对应的奖励积分值,每一行包含三个正整数,第1个和第2个整数表示电子元件编号(从1开始),第3个整数表示两个元件之间搭建电路的奖励积分num(num<1e9)。整数之间用空格隔开。
输出
每组输出占1行,输出一个正整数,即最多可以得到的积分奖励值。如果没有办法把所有元件全部连接起来,则输出“No solution.”。
样例输入 Copy
3 3
1 2 10
1 3 20
2 3 30
样例输出 Copy
50
分析:这题和上题一样的东西,就是求个最大值,把排序换一下就可以了。
啊这,不知道为什么,这道题,我用c语言做不出来。
总是运行错误。又是上次的那种错误,看别人的,本质上一摸一样,但是看了近3个小时了,还是不行。唉,copy了别人的代码
代码实现:c++
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct shu
{
int x;
int y;
int bian;
};
bool cmp(shu a,shu b){
return a.bian>b.bian;
}
int parents[2000];
int rankk[2000];
shu a[2000];
ll sum;
int find_set(int x){
if(x!=parents[x])
parents[x]=find_set(parents[x]);
return parents[x];
}
int union_set(int x,int y,int bian){
x=find_set(x);
y=find_set(y);
if(x==y)
return 0;
sum+=bian;
if(rankk[x]>rankk[y]){
parents[y]=x;
}
else{
parents[x]=y;
if(rankk[x]==rankk[y]){
rankk[y]++;
}
}
return 1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin>>n>>m;
for (int i=0;i<=n;i++)
{
parents[i]=i;
rankk[i]=0;
}
for(int i=0;i<m;i++){
cin>>a[i].x>>a[i].y>>a[i].bian;
}
sum=0;
sort(a,a+m,cmp);
int flag = 0;
for (int i=0; i<m;i++)
{
int res=union_set(a[i].x,a[i].y,a[i].bian);
if(res){
flag++;
}
}
if(flag==n-1)
cout<<sum<<endl;
else
cout<<"No solution."<<endl;
return 0;
}
F 单源最短路径问题
题目描述
编程实现Dijkstra算法,求一个有向加权图中,从源点出发到其他各个顶点的最短路径。
输入
第1行第1个值表示顶点个数,第2个值表示边个数;第2行开始为边(两个顶点,边的起点和终点)及权重。
输出
顶点0到每一个顶点的最短路径长度。
样例输入 Copy
5 7
0 1 10
0 3 30
0 4 100
1 2 50
2 4 10
3 2 20
3 4 60
样例输出 Copy
0 10 50 30 60
分析:题目说了啊,要用Dijkstra算法,求0点到 其他点的最短距离
额,直接说思路。
先初始化,把图上的所有的点与点之间的距离都设置为 无穷大(INF);
然后 再一个一个读入 img数组中。
然后就可以启动我们的 djikstra 方法;
这个方法嘞,先还是初始化,所有的点都没有被用到。used[i]=0;
另外用一个数组来表示 0点到 所有点的距离 dis[i]=img[0][i];
然后进行 n-1次 循环操作,每次都要找出距离最短的点,并进行更新操作。
其他的内容就是 djikstra的了,上课认真听讲。
代码实现:c语言
#include <stdio.h>
#include <stdlib.h>
int INF=10000;
int img[200][200];
int main (){
int n;
int m;
while(~scanf("%d %d",&n,&m)){
init(n);
int x;
int y;
int bian;
for(int i=0;i<m;i++)
{
scanf("%d %d %d",&x,&y,&bian);
img[x][y]=bian;
}
dijkstra(n);
}
return 0;
}
void init (int n){
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
img[i][j]=INF;
img[0][0]=0;
}
void dijkstra(int n){
int dis[200];
int used[200];
for(int i=0;i<n;i++)
{
dis[i]=img[0][i];
used[i]=0;
}
for(int i=1;i<n;i++){
int min=INF;
int k;
for(int j=0;j<n;j++)
{
if((!used[j])&&dis[j]<min){
min=dis[j];
k=j;
}
}
used[k]=1;
for(int j=0;j<n;j++)
{
if((!used[j])&&(dis[k]+img[k][j]<dis[j])){
dis[j]=dis[k]+img[k][j];
}
}
}
for(int i=0;i<n;i++)
printf("%d ",dis[i]);
printf("\n");
}