ACM之近期学习总结

最近学了最短路径算法,就看了几道题,另外还做了洛谷上的一些题,接下来总结一下近期所学。

最短路径算法:

Dijkstra算法 O (N2)
用处:用来计算从一个点到其他所有点的最短路径的算法,是一种单源最短路径算法。也就是说,只能计算起点只有一个的情况。
Dijkstra的时间复杂度是O (N2),它不能处理存在负边权的情况。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define Inf 0x3f3f3f3f
using namespace std;
int map[1005][1005];
int vis[1005],dis[1005];
int n,m;//n个点,m条边
 
void Init ()
{
	memset(map,Inf,sizeof(map));
	for(int i=1;i<=n;i++)
	{
		map[i][i]=0;
	}
}
 
void Getmap()
{
	int u,v,w;
    for(int t=1;t<=m;t++)
	{
	  	scanf("%d%d%d",&u,&v,&w);
	  	if(map[u][v]>w)
		  {
		  map[u][v]=w;
		  map[v][u]=w;
	      }
	}	
}
 
void Dijkstra(int u)
{
	memset(vis,0,sizeof(vis));
	for(int t=1;t<=n;t++)
	{
		dis[t]=map[u][t];
	}
	vis[u]=1;
	for(int t=1;t<n;t++)
	{
		int minn=Inf,temp;
		for(int i=1;i<=n;i++)
		{
			if(!vis[i]&&dis[i]<minn)
			{
				minn=dis[i];
				temp=i;
			}
		}
		vis[temp]=1;
		for(int i=1;i<=n;i++)
		{
			if(map[temp][i]+dis[temp]<dis[i])
			{
				dis[i]=map[temp][i]+dis[temp];
			}
		}
	}
	
}
 
int main()
{
	
	scanf("%d%d",&m,&n);
	Init();
	Getmap();
	Dijkstra(n);
	printf("%d\n",dis[1]);
	return 0;
}

并查集:

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。在一些题目中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,所以只能用并查集来描述。

搞笑理解:

关于并查集和路径压缩:

有a,b,c三个人

假设a和b打架了,a做了b的小弟。则令f[a]=b;

后来a打赢了c 黑社会

那么c就是a的小弟了。所以,令f[c]=a;

但是,c不知道b,这不符合要求。

所以,我们必须让c的大哥变成最大的老大。

定义函数find

int find(int k){
    if(f[k]==k)return k;
    return find(f[k]);
}

f[c]=find(a);

这时,我们可以使途中经过的人的大哥也变成老大。

//路径压缩
int find(int k){
    if(f[k]==k)return k;
    return f[k]=find(f[k]);
}

f[c]=find(a);

而判定两个人的老大是否相等,只需用

if(find(a)==find(b))

(在其中,一个人不能有两个老大。当已经有老大的人臣服时,老大也将成为胜利的人的小弟)

 

例题:

现在有一个并查集,你需要完成合并和查询操作。

输入格式:

第一行包含两个整数N、M,表示共有N个元素和M个操作。

接下来M行,每行包含三个整数Zi、Xi、Yi

当Zi=1时,将Xi与Yi所在的集合合并

当Zi=2时,输出Xi与Yi是否在同一集合内,是的话输出Y;否则话输出N

输出格式:

如上,对于每一个Zi=2的操作,都有一行输出,每行包含一个大写字母,为Y或者N

 

代码:

#include<bits/stdc++.h>
using namespace std;
int i,j,k,n,m,s,ans,f[10010],p1,p2,p3;
//f[i]表示i的集合名
int find(int k){
    //路径压缩
    if(f[k]==k)return k;
    return f[k]=find(f[k]);
}
int main()
{
    cin>>n>>m;
    for(i=1;i<=n;i++)
        f[i]=i;//初始化i的老大为自己
    for(i=1;i<=m;i++){
        cin>>p1>>p2>>p3;
        if(p1==1)
            f[find(p2)]=find(p3);
            //p3打赢了p2
        else
            if(find(p2)==find(p3))
            //是否是一伙的
                printf("Y\n");
            else
                printf("N\n");
    }
    return 0;
}

 

不熟知识点总结:

一.ASCII码

65~90为26个大写英文字母,97~122号为26个小写英文字母

例:将一个十进制的数转化为大于十进制的进制数时,必然会用‘A’,   ‘B’,‘C’等分别表示10,11,12等。

将一个十进制的数字x转化为其他进制数的方法之一:

while(x){
      e=x%n;
      if(e>=10)
      {
         w[k++]=char(e+55);//或者w[k++]=(char)(s[i]-10+'A');
      }
      else w[k++]=e+'0';
      x/=n;
      }

 

二.string类型的push_back

 push_back,在vector类中作用为在vector尾部加入一个数据。
 string中也有这个函数,作用是字符串之后插入一个字符。

 

三.cout控制输出小数点后位数

cout<<setiosflags(ios::fixed)<<setprecision(9);
cout<<result<<endl;//表示让输出的result保留9位小数

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值