海亮Day1:最短路专题

第一天来海亮,这真是个学习(吃喝)、刷题(玩乐)的好地方!!!废话不多说,进行今天的总结。。。

最短路

Floyed
先粘题目(板子题):POJ 3360
直接上代码:

#include<bits/stdc++.h>//谁想打那么长的数据库O^O
using namespace std;
int n,m,ans,xx,yy;
int a[1000][1000],d[1000][1000];
void floyed()
{
	for(int k=1;k<=n;k++)//最重要的k循环放在最外面
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	a[i][j]=a[i][j]|a[i][k]&a[k][j];
	//floyed的核心代码,就好比是三角形两边之和大于第三边
	//floyed是个O(n3)的做法,只能解决小数据,在解图论题时是个暴力的万能算法
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	a[i][j]=0;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		cin>>x>>y;
		a[x][y]=1;
	}
	floyed();
	for(int i=1;i<=n;i++)
	{
		int w=0;
		for(int j=1;j<=n;j++)
		if(a[i][j]==1||a[j][i]==1)
		w++;
		if(w==n-1) ans++;
	}
	cout<<ans;
	return 0;
}

Djkstra

dijkstra主要是以起始点为中心向外层层扩展,直到扩展到终点为止,显然这是一个 O(n2)的算法,倘若用堆优化一下,就变成了O(mlogn)的优秀算法。(突然想到当初在机房学dijkstra用了好几天,还是去集训学的效率高)

typedef pair <int,int> pii;
priority_queue <pii,vector<pii>,greater<pii> > Q;
//堆优化的dijkstra
void dijkstra()
{
	memset(d,127,sizeof(d));
	d[1]=0;
	Q.push(mp(0,1));//mp就是pair( , )
	while(Q.size())
	{
		int x=Q.top().second; Q.pop();
		if(v[x]) continue;
		v[x]=true;
		for(int i=last[x];i;i=e[i].next)
		//最基本的邻接表边的遍历
		{
			int y=e[i].too,z=e[i].z;
			if(d[y]>d[x]+z)
			{
				d[y]=d[x]+z;
				Q.push(mp(-d[y],y));
			}
		}
	}
}

放个板子题:HYSBZ - 4152

#include<bits/stdc++.h>//谁想打那么长的数据库O^O
using namespace std;
bool v[210000];
int n,tot,d[210000];
struct see {int x,y,c,next,last;}a[810000];
void add(int x,int y,int c)
{
    a[++tot].x=x;a[tot].y=y;a[tot].c=c;
    a[tot].next=a[x].last;a[x].last=tot;
}
struct zuobiao {int x,y,qq;}g[210000];
bool cmg1(see a,see b){return a.x<b.x;}
bool cmg2(see a,see b){return a.y<b.y;}
struct gg
{
    int x;
   	friend bool operator <(gg n1,gg n2)
    {return d[n1.x]>d[n2.x];}
};
priority_queue<gg> Q;
void dijkstra()
{
    memset(d,100,sizeof(d));
    memset(v,false,sizeof(v));
	d[1]=0;
	v[1]=true;
    gg tmp;
    tmp.x=1;Q.push(tmp);
    while(!Q.empty())
    {
        tmp=Q.top();int x=tmp.x;
        for(int k=a[x].last;k;k=a[k].next)
        {
            int y=a[k].y;
            if(d[y]>d[x]+a[k].c)
            {
                d[y]=d[x]+a[k].c;
                if(v[y]==false)
                {
                    v[y]=true; gg ss;
                    ss.x=y; Q.push(ss);
                }
            }
        }
        Q.pop(); v[x]=false;
    }
}//就是一个dijkstra+堆优化的板子题
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
	scanf("%d%d",&g[i].x,&g[i].y),g[i].qq=i;
    sort(g+1,g+1+n,cmg1);
    for(int i=1;i<=n;i++)
	add(g[i].qq,g[i+1].qq,g[i+1].x-g[i].x),
	add(g[i+1].qq,g[i].qq,g[i+1].x-g[i].x);
    sort(g+1,g+1+n,cmg2);
    for(int i=1;i<=n;i++)
	add(g[i].qq,g[i+1].qq,g[i+1].y-g[i].y),
	add(g[i+1].qq,g[i].qq,g[i+1].y-g[i].y);
	//由于题目上只给了坐标,所以要分x,y排序(很恶心的排序,并不想写它)
    dijkstra();
    printf("%d\n",d[n]);
    return 0;
}

SPFA

一个在中国非常流行的最短路算法,·很好的解决了dijkstra无法判断负环的算法(还有Bell-ford,但我不是很清楚,只是听说SPFA是它的队列优化,就直接学了spfa。。。)

void spfa()
{
	queue<int>Q;
	Q.push(0);//从0开始跑最短路,并把0放入队列
	v[0]=true;//访问数组
	d[0]=0; //记录最短路路程
	while(!Q.empty())
	{
		int x=Q.front(); Q.pop(); v[x]=false;
		for(int i=last[x];i;i=e[i].next)
		{
			int y=e[x].ver,z=e[i].edge;
			if(d[y]>d[x]+z)
			{
				d[y]=d[x]+z;
				if(!v[y])
				v[y]=true,Q.push(y);
			}
		}
	}
}

放一个SPFA和基础数论结合的题:
HYSBZ - 2118
很有意思的一个最短路题。。。(不想吐槽)
题解:(之后再补充>_<,请相信我!!!)

#include<bits/stdc++.h>//谁想打那么长的数据库O^O
#define sea  500010
#define ll long long
using namespace std;
bool v[sea]={0};
int a[20],n,mm=(1<<30)-1;
ll l,r,d[sea];//瞟一眼数据就必须要开ll
void spfa()
{
	queue<int>Q;
	Q.push(0);
	v[0]=true;
	d[0]=0; 
	int x,y;
	while(!Q.empty())
	{
		x=Q.front(); Q.pop(); v[x]=false;
		for(int i=1;i<=n;i++)
		{
			y=(x+a[i])%mm;
			if(d[y]>d[x]+a[i])
			{
				d[y]=d[x]+a[i];
				if(!v[y])
				v[y]=true,Q.push(y);
			}
		}
	}
}//spfa在这里面就是个板子(它在哪不是板子。。)
ll  cf(ll  x)//差分
{
	ll ans=0;
	for(int i=0;i<mm;i++)
	if(d[i]<=x)  ans+=(x-d[i])/mm+1;
	return ans;
} 
int main()
{
	cin>>n>>l>>r;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		if(a[i]==0) 
		{
			i--,n--;
			continue;
		} 
		mm=min(a[i],mm);
	}
	for(int i=0;i<mm;i++)
    d[i]=100000000000000000LL;
    spfa();
    cout<<cf(r)-cf(l-1);
	return 0;
} 

然后是一个新颖 的知识:分层图
先上一个题:HYSBZ - 2662
分层图:
在这里插入图片描述

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#define mp make_pair
using namespace std;
int tot,n,m,k,last[220009],d[220009];
struct see{int z,too,next;}e[4200009];
bool v[220009];
void add(int x,int y,int z){e[++tot].too=y; e[tot].z=z; e[tot].next=last[x]; last[x]=tot;}
typedef pair <int,int> pii;
priority_queue <pii,vector<pii>,greater<pii> > Q;
void dijkstra()
{
	memset(d,127,sizeof(d));
	d[1]=0;
	Q.push(mp(0,1));
	while(Q.size())
	{
		int x=Q.top().second; Q.pop();
		if(v[x]) continue;
		v[x]=true;
		for(int i=last[x];i;i=e[i].next)
		{
			int y=e[i].too,z=e[i].z;
			if(d[y]>d[x]+z)
			{
				d[y]=d[x]+z;
				Q.push(mp(d[y],y));
			}
		}
	}
}//也是个dijkstra+堆优化的板子
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for (int i=1;i<=m;++i)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
		for (int j=1;j<=k;++j)
		{
			add(j*n+x,j*n+y,z);
			add(j*n+y,j*n+x,z);
			add((j-1)*n+x,j*n+y,z/2);
			add((j-1)*n+y,j*n+x,z/2);
			//在原图上又加了一层,具体可以看上图
		}
	} 
	dijkstra();
    int ans=d[n];
    for (int i=0;i<=k;++i)
    ans=min(ans,d[i*n+n]);
    cout<<ans;
	return 0;
}

双倍经验:P4568 [JLOI2011]飞行路线

#include<bits/stdc++.h>
#define sea 1000010
using namespace std;
int n,m,k,s,t,a,b,c,d[sea],last[sea],tot=0;
bool vis[sea];
inline int read()
{
	int s=0,w=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1; ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
struct see{int ver,next,edge;}e[6000060];
void add(int x,int y,int z){e[++tot].ver=y;e[tot].edge=z;e[tot].next=last[x];last[x]=tot;}
void dijkstra(int s)
{
    memset(d,0x3f,sizeof(d)); d[s]=0;
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > Q;
    Q.push(make_pair(0,s));
    while(!Q.empty())
    {
        int x=Q.top().second; Q.pop();
        if(!vis[x])
        {
            vis[x]=1;
            for(int i=last[x];i;i=e[i].next)
            {
                int y=e[i].ver;
                if(d[y]>d[x]+e[i].edge) 
                {
                    d[y]=d[x]+e[i].edge;
                    Q.push(make_pair(d[y],y));
                }
            }
        }
    }
}
int main()
{
	n=read(); m=read(); k=read(); s=read(); t=read();
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		x=read(); y=read(); z=read();
		add(x,y,z),add(y,x,z);
		for (int j=1;j<=k;++j)
		{
			add((j-1)*n+x,j*n+y,0);
			add((j-1)*n+y,j*n+x,0);
			add(j*n+x,j*n+y,z);
			add(j*n+y,j*n+x,z);
		}
	}
	for(int i=1;i<=k;++i) add(t+(i-1)*n,t+i*n,0);
	dijkstra(s);
	printf("%d",d[t+k*n]);
	return 0;
} 

大概好像我学到的就这么点了,老师讲的好像也就这么多吧0.0(冰山效应)。
愉快的结束了第一天,尽管没有写完题,但很开心,学到了很多,,,总结就这样吧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值