Luogu P1828 香甜的黄油-Sweet Butter (图论-Floyd/SPFA)

来源:Luogu P1828,JZOJ #316

题目描述

农夫John发现做出全威斯康辛州最甜的黄油的方法:糖。把糖放在一片牧场上,他知道N(1<=N<=500)只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。当然,他将付出额外的费用在奶牛上。

农夫John很狡猾。像以前的Pavlov,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。

他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶。 农夫John知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。

给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场(他将把糖放在那)

解题思路

  • 这道图论题可以用两种最短路算法来解决: F l o y d Floyd Floyd S P F A SPFA SPFA
  • 先讲一下 F l o y d Floyd Floyd ,很显然,纯粹的三重循环枚举肯定会炸,但是其实在循环中可以有一个优化,因为这是无向图, i i i j j j j j j i i i是一样的,那么第三重循环 j j j 枚举到 i i i 就行,这样就能 A C AC AC 了;
  • 还有 S P F A SPFA SPFA ,其实这道题要进行 p p p S P F A SPFA SPFA,枚举每一个牧场为集中点,累加得所有牛到达的路程和,取最小值;

代码君

可爱的Floyd:
#include 
using namespace std;
int a[1000][1000];
int sum[1000];
int main()
{
	freopen("input.in","r",stdin);
	freopen("output.out","w",stdout);
	int n,m,c;
	scanf("%d %d %d",&n,&m,&c);
	for (int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		sum[x]++;  //统计x号牧场的牛数量
	}
	memset(a,10,sizeof(a));  //初值
	for (int i=1;i<=c;i++)
	{
		int x,y,v;
		scanf("%d %d %d",&x,&y,&v);
		a[x][y]=min(a[x][y],v);  //邻接矩阵存储重边
		a[y][x]=min(a[y][x],v);
	}
	for (int i=1;i<=m;i++) a[i][i]=0;  //初值
	for (int k=1;k<=m;k++)
	 for (int i=1;i<=m;i++)
	  for (int j=1;j<i;j++)
	   if (a[i][k]+a[k][j]<a[i][j])  //迭代
	   {
	   		a[i][j]=a[i][k]+a[k][j];
	   		a[j][i]=a[i][k]+a[k][j];
	   }
	int ans=0xfffffff;
	for (int i=1;i<=m;i++)
	{
		int s=0;
		for (int j=1;j<=m;j++) s+=a[i][j]*sum[j];  //累加路程
		ans=min(ans,s);  //取最小值
	}
	printf("%d",ans);
	return 0;
}
然后是英俊的SPFA:
#include 
using namespace std;
int t=0,n,p,c;
int linkk[1500],dis[1500],vis[1500],q[10000],a[1500];
struct node
{
	int y,v,next;
}e[5005];
void insert(int x,int y,int v)
{
	e[++t].y=y; e[t].v=v;
	e[t].next=linkk[x]; linkk[x]=t; 
}
void SPFA(int k)  //SPFA模板
{
	memset(dis,10,sizeof(dis));  //初值
	memset(vis,0,sizeof(vis));  //清空标记数组,vis[k]表示点k是否在队列中
	dis[k]=0;
	vis[k]=1;
	int head=1,tail=1;
	q[head]=k;  //点k进队
	for (head=1;head<=tail;head++)
	{
		int x=q[head];  //取出队头
		for (int i=linkk[x];i>0;i=e[i].next)  //邻接表查询
		{
			if (dis[x]+e[i].v<dis[e[i].y])  //迭代
			{
				dis[e[i].y]=dis[x]+e[i].v;
				if (vis[e[i].y]==0)  //若此点没有标记,标记,进队
				{
					vis[e[i].y]=1;
					q[++tail]=e[i].y;
				}
			}
		}
		vis[x]=0;  //出队
	}
}
int main()
{
	freopen("input.in","r",stdin);
	freopen("output.out","w",stdout);
	scanf("%d %d %d",&n,&p,&c);
	for (int i=1;i<=n;i++) scanf("%d\n",&a[i]);
	for (int i=1;i<=c;i++)
	{
		int xx,yy,vv;
		scanf("%d %d %d",&xx,&yy,&vv);
		insert(xx,yy,vv);  //邻接表存储
		insert(yy,xx,vv);  //双向边
	}
	int ans=0xfffffff;
	for (int i=1;i<=p;i++)
	{
		SPFA(i);  //对每个牧场SPFA
		int s=0;
		for (int j=1;j<=n;j++) s+=dis[a[j]];  //累加路程
		ans=min(ans,s);  //取最小值
	}
	printf("%d",ans);
	return 0;
}

你可能感兴趣的:(#,图论-最短路,图论)