图论-存储&最短路

今天讲图论......

基础就开心地跳过

图的存储

邻接矩阵没什么,注意一下初始化就好了,主要是邻接表。

曾经pascal用的指针,现在用的是静态链表,感觉很six。

const int MAXN=10010,MAXM=10010; 
struct line
{
	int y,next;//如果有权值还要再开一个变量
}l[MAXM];
int n,m,f[MAXN]={0},num=0;
inline void init()
{
	int a,b;
	scanf("%d%d",&n,&m);//n个顶点,m条边 
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d",&a,&b);
		l[++num].y=b;//加入新的一个节点
		l[num].next=f[a];
		f[a]=num; //这里num可直接用i,无向图要反过来再存储一次 
	}
	return;
}
图的最短路

有很多种做法......

洛谷p1529回家

我用的Floyed(当然了,这题有很多算法)它的思想类似dp,但是时间复杂度很高,数据范围大就会gg。

Floyed的k在最外层,个人理解是一个个加入顶点,判断能否松弛。

#include<bits/stdc++.h>
using namespace std;
int s[60]={0},f[55][55],p;
inline void init()
{
    memset(f,10,sizeof(f));//用邻接矩阵存储
    char x,y,t[10];
    int xx,yy,z;
    scanf("%d",&p);
	gets(t);
    for(int i=1;i<=p;i++)//读入很恶心,啊
    {
        scanf("%c %c %d",&x,&y,&z);
        if(x>='A'&&x<='Z')s[x-'A'+1]=1,xx=x-'A'+1;
        else xx=x-'a'+27;
        if(y>='A'&&y<='Z')s[y-'A'+1]=1,yy=y-'A'+1;
        else yy=y-'a'+27;
        if(z<f[xx][yy])f[xx][yy]=f[yy][xx]=z;
        gets(t);
    }
    s[26]=0;
    for(int i=1;i<=52;i++)f[i][i]=0;
    return;
}

inline void floyd()
{
	for(int k=1;k<=52;k++)//k在最外层哦
	for(int i=1;i<=52;i++)
	for(int j=1;j<=52;j++)
	f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
	return;
}

inline void print()
{
    int mi=100000,k;
    for(int i=1;i<=51;i++)
    if(s[i]==1&&f[i][26]<mi)
    {
    	mi=f[i][26];
    	k=i;
    }
    printf("%c %d",'A'+k-1,mi);
    return;
}

int main()
{
	init();
	floyd();
	print();
	return 0;
}

洛谷p1339热浪

来一个dijkstra吧~

不断把离源点最近的点加入已搜索的行列(?

然后用这个点更新其余点的最短距离,不断重复。

#include<bits/stdc++.h>
using namespace std;
int ans[2600][2600],n,m,b,e,sum[2600]={0};
int flag[2600]={0};
inline void init()
{
    scanf("%d%d%d%d",&n,&m,&b,&e);
    memset(ans,10,sizeof(ans));
    for(int i=1;i<=n;i++)ans[i][i]=0;
    for(int i=1;i<=m;i++)//为了初始化方便用的邻接矩阵
    {
        int x,y,v;
        scanf("%d%d%d",&x,&y,&v);
        if(ans[x][y]>v)ans[x][y]=ans[y][x]=v;
    }
    return;
}

inline void di()
{
    for(int i=1;i<=n;i++)sum[i]=ans[b][i];//初始化
    flag[b]=1;
    for(int i=1;i<n;i++)
    {
        int mi=100000,k=0;
        for(int j=1;j<=n;j++)//贪心一波
        if(flag[j]==0&&sum[j]<mi)
        {
            mi=sum[j];
            k=j;
        }
        if(k==0)return;
        flag[k]=1;//加入已搜队伍
        for(int j=1;j<=n;j++)//更新其余点最短距离
        if(flag[j]==0)sum[j]=min(sum[j],sum[k]+ans[j][k]);
        if(flag[e]==1)return;
    }
    return;
}

int main()
{
    init();
    di();
    printf("%d",sum[e]);
    return 0;
}

洛谷p1828香甜的黄油

就决定是你了,Bellman-ford!

这题刚开始害怕复杂度,但是裸的Bellman-ford居然过了,emmmm......

bellman-ford的思想是用不断每一条边更新最短距离直到不再更新,

如果更新n次后仍未更新完,则说明有负权环(一条路径最多经过n个点,含n-1条边)。

#include<bits/stdc++.h>
using namespace std;
struct line
{
	int l,r,w;
}l[1500];
int s[900][900],a[900]={0},n,p,c,flag[900],sum[900],num=0;
int ans=1000000;
inline void init()
{
	scanf("%d%d%d",&n,&p,&c);
	for(int i=1;i<=n;i++)
	{
		int t;
		scanf("%d",&t);
		a[t]++;//计算每个牧场的奶牛数
	}
	for(int i=1;i<=p;i++)
	for(int j=1;j<=p;j++)s[i][j]=100000;
	for(int i=1;i<=p;i++)s[i][i]=0;
	for(int i=1;i<=c;i++)
	{
		int x,y,v;
		scanf("%d%d%d",&x,&y,&v);
		if(s[x][y]>v)//为了不重复,弄了个实际没什么用的邻接矩阵
		{
		    s[x][y]=s[y][x]=v;
		    l[++num].l=x;
		    l[num].r=y;
		    l[num].w=v;
		    l[++num].r=x;
		    l[num].l=y;
		    l[num].w=v;
		}
	}
	return;
}

inline void bell(int b)
{
	for(int i=1;i<=p;i++)sum[i]=100000;
	sum[b]=0;
	for(int i=1;i<=p;i++)
	{
		bool re=false;
		for(int j=1;j<=num;j++)
		if(sum[l[j].l]+l[j].w<sum[l[j].r])
		{
			sum[l[j].r]=sum[l[j].l]+l[j].w;
			re=true;
		}
		if(re==false)break;
	}
	int ss=0;
	for(int i=1;i<=p;i++)ss+=a[i]*sum[i];
	ans=min(ans,ss);
	return;
}

int main()
{
        init();
	for(int i=1;i<=p;i++)di(i);
	printf("%d",ans);
	return 0;
}

洛谷p3371模版题

这题我用了SPFA,思路是运用队列优化Bellman-ford,

如果当前加入的边可以松弛,则将该边的末端点加入队列等待松弛。

#include<bits/stdc++.h>
using namespace std; 
const int MAXN=10010,MAXM=500010; 
struct line
{
	int y,next,w;
}l[MAXM];
int n,m,f[MAXN]={0},num=0,begin;
int ans[MAXN],flag[MAXN]={0},q[MAXN*MAXN]={0};
inline void init()
{
	int a,b,v;
	scanf("%d%d%d",&n,&m,&begin);
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d%d",&a,&b,&v);
		l[++num].y=b;
		l[num].w=v;
		l[num].next=f[a];
		f[a]=num;
	}
	return;
}

inline void spfa(int b)
{
	ans[b]=0;
	flag[b]=1;
	int head=1,tail=1;
	q[head]=b;
	while(head<=tail)
	{
		int t=q[head];
		for(int i=f[t];i;i=l[i].next)
		if(ans[t]+l[i].w<ans[l[i].y])
		{
			ans[l[i].y]=ans[t]+l[i].w;
			if(flag[l[i].y]==0)
			{
				q[++tail]=l[i].y;
				flag[l[i].y]=1;
			}
		}
		head++;flag[t]=0;//出队后要改变该点为可入队!!! 
	}
	return;
}
			

int main()
{
	init();
	for(int i=1;i<=n;i++)ans[i]=2147483647;
	spfa(begin);
	for(int i=1;i<n;i++)printf("%d ",ans[i]);
	printf("%d",ans[n]);
	return 0;
}
有时间写个比较吧(flag


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值