前言


首先恭喜自己,终于写出了能在POJ上排第一名的代码。其次为自己惋惜下,判重时少了个“j!=x”的条件,找了n久才发现这个小bug,这要是在赛场上,肯定悔死了。首先用spfa做,怎么都找不出错误,甚至以为自己模板写错了,再用dijkstra做,发现依然wa,才觉得不是模板问题。

题目大意


哥伦布要从野人手里买20种货物。用以下四种种方法可以买到货物:

  • 按货物的价格(以金币为单位)付足金币。
  • 对于每个货物,可以用一个玻璃珠代替一个金币。
  • 用等价的其他种类货物交换。
  • 用价格更低的货物加上金币来交换。

问每种物品至少需要花多少金币才能买到。

思路


最短路,将每个物品抽象成节点,源点是虚拟的,源点与其他节点之间的边的权值就是物品价格。

注意物品价格相等时可以交换的条件,也就是说,此时两个节点应用一条权值为0的边相连。

代码


首先是用spfa写出的代码:

#include<iostream>
#include<queue>
using namespace std;
#define datasize 20+1
#define maximum 2147483647
int map[datasize+1][datasize+1],d[datasize+1],mark[datasize+1],n,m;
void initialize_single_source(int s)
{
	for(int i=1;i<=n;i++)
		d[i]=maximum;
	d[s]=0;
}
void relax(int u,int v)
{
	if(d[v]>d[u]+map[u][v])
		d[v]=d[u]+map[u][v];
}
void spfa(int s)
{
	initialize_single_source(s);
	queue<int> q;
	for(int i=1;i<=n;i++)
		mark[i]=false;
	q.push(s);
	mark[s]=true;
	while(q.size())
	{
		int u=q.front();
		q.pop();
		mark[u]=false;
		for(int i=1;i<=n;i++)
			if(i!=u&&map[u][i]!=maximum)
			{
				int temp=d[i];
				relax(u,i);
				if(d[i]<temp&&!mark[i])
				{
					q.push(i);
					mark[i]=true;
				}
			}
	}
}
bool check(int x)
{
	for(int i=2;i<=n;i++)
		for(int j=2;j<=n;j++)
			if(i!=x&&j!=i&&j!=x&&d[x]==d[i]+d[j])
				return true;
	return false;
}
int main()
{
	int t;
	for(scanf("%d",&t);t>0;t--)
	{
		scanf("%d",&n);
		n++;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				map[i][j]=maximum;
		for(int i=0;i<n-1;i++)
		{
			int temp_1,temp_2;
			scanf("%d%d",&temp_1,&temp_2);
			map[1][temp_1+1]=temp_2-1;
		}
		scanf("%d",&m);
		for(int i=0;i<m;i++)
		{
			int temp_1,temp_2,temp_3;
			scanf("%d%d%d",&temp_1,&temp_2,&temp_3);
			map[temp_1+1][temp_2+1]=temp_3;
		}
		for(int i=2;i<=n;i++)
			for(int j=i+1;j<=n;j++)
				if(map[1][i]==map[1][j])
					map[i][j]=map[j][i]=0;
		spfa(1);
		for(int i=0;i<n-1;i++)
			printf("%d %d\n",i+1,d[i+2]);
		int sum=0;
		for(int i=2;i<=n;i++)
			if(check(i))
				sum++;
		printf("%d\n",sum);
	}
}

接着是dijkstra(堆优化)的做法:

#include<iostream>
#include<stdio.h>
using namespace std;
#define datasize 20+1
#define maximum 2147483647
int a[datasize+1],heap_size_a,v[datasize+1],map[datasize+1][datasize+1],length_a,n,m,result[datasize+1];
int parent(int i)
{
	return i/2;
}
int left(int i)
{
	return 2*i;
}
int right(int i)
{
	return 2*i+1;
}
void min_heapify(int a[],int i)
{
	int l=left(i),r=right(i),least;
	if(l<=heap_size_a&&a[l]<a[i])
		least=l;
	else
		least=i;
	if(r<=heap_size_a&&a[r]<a[least])
		least=r;
	if(least!=i)
	{
		swap(a[i],a[least]);
		swap(v[i],v[least]);
		min_heapify(a,least);
	}
}
void build_min_heap(int a[])
{
	heap_size_a=length_a;
	for(int i=length_a/2;i>=1;i--)
		min_heapify(a,i);
}
int heap_extract_min(int a[])
{
	int min=v[1];
	result[v[1]]=a[1];
	a[1]=a[heap_size_a];
	v[1]=v[heap_size_a];
	heap_size_a--;
	min_heapify(a,1);
	return min;
}
void heap_decrease_key(int a[],int i,int key)
{
	a[i]=key;
	while(i>1&&a[parent(i)]>a[i])
	{
		swap(a[i],a[parent(i)]);
		swap(v[i],v[parent(i)]);
		i=parent(i);
	}
}
void initialize_single_source(int s)
{
	length_a=n;
	for(int i=1;i<=length_a;i++)
	{
		a[i]=maximum;
		v[i]=i;
	}
	a[s]=0;
}
void relax(int i,int j)
{
	if(a[j]>result[i]+map[i][v[j]])
		heap_decrease_key(a,j,result[i]+map[i][v[j]]);
}
void dijkstra(int s)
{
	initialize_single_source(s);
	build_min_heap(a);
	while(heap_size_a>=1)
	{
		int u=heap_extract_min(a);
		for(int i=1;i<=heap_size_a;i++)
			if(map[u][v[i]]!=maximum)
				relax(u,i);
	}
}
bool check(int x)
{
	for(int i=2;i<=n;i++)
		for(int j=2;j<=n;j++)
			if(i!=x&&j!=i&&j!=x&&result[x]==result[i]+result[j])
				return true;
	return false;
}
int main()
{
	int t;
	for(scanf("%d",&t);t>0;t--)
	{
		scanf("%d",&n);
		n++;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				map[i][j]=maximum;
		for(int i=0;i<n-1;i++)
		{
			int temp_1,temp_2;
			scanf("%d%d",&temp_1,&temp_2);
			map[1][temp_1+1]=temp_2-1;
		}
		scanf("%d",&m);
		for(int i=0;i<m;i++)
		{
			int temp_1,temp_2,temp_3;
			scanf("%d%d%d",&temp_1,&temp_2,&temp_3);
			map[temp_1+1][temp_2+1]=temp_3;
		}
		for(int i=2;i<=n;i++)
			for(int j=i+1;j<=n;j++)
				if(map[1][i]==map[1][j])
					map[i][j]=map[j][i]=0;
		dijkstra(1);
		for(int i=0;i<n-1;i++)
			printf("%d %d\n",i+1,result[i+2]);
		int sum=0;
		for(int i=2;i<=n;i++)
			if(check(i))
				sum++;
		printf("%d\n",sum);
	}
}