【Vijos-P1285】佳佳的魔法药水-Dijkstra思想
题目大意:有N种药水,以0~N-1编号,给出若干个配方,格式为A+B=C(即一个A药水和一个B药水可以合成一个C药水),可以从商店里花费一些钱来买药水,每种药水的价格不一定相同,要配出一个0号药水,求最小花费以及能满足最小花费的方案个数。
做法:我们可以用Dijkstra的思想,用已知来更新未知。我们用一个v数组来标记一种药水的最小花费是否确定,如果v[i]为真,则表示i号药水的最小花费已经确定,否则反之。同时,用cost[i]和ans[i]记录当前求出的i号药水的最小花费和满足最小花费的方案个数,f[i][j]记录一个i号药水和一个j号药水能够合成的药水编号(是不是很像邻接矩阵?),cost[i]初始化为药水在商店中的价格,ans[i]初始化为1。每次选择一个v[k]为假并且cost[k]最小的k,可以证明这个k号药水再没有其他方案使得它的最小花费更小了,然后寻找与它相关联的配方,如果k号药水可以跟另一个最小花费已经确定的j号药水合成一种药水,则更新cost[f[j][k]]和ans[f[j][k]]:如果cost[j]+cost[k]<cost[f[j][k]],则将cost[f[j][k]]更新为cost[j]+cost[k],并将ans[i]更新为1;如果cost[j]+cost[k]=cost[f[j][k]],则将ans[f[j][k]]加上ans[j]*ans[k]。更新完所有和k号药水有关的配方之后,将v[k]赋值为真,重复上述过程直到所有药水都更新过为止。最后输出cost[0]和ans[0]即可。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
long n;
long f[1010][1010]={0},cost[1010]={0},ans[1010]={0};
void dijkstra()
{
long v[1010]={0},k,minimum;
for(int i=1;i<=n;i++)
{
minimum=99999999;
for(int j=0;j<n;j++)
if (!v[j]&&cost[j]<minimum) {k=j;minimum=cost[j];}
if (minimum==99999999) break;
v[k]=1;
for(int j=0;j<n;j++)
if (v[j]&&f[j][k]>=0)
{
if (cost[f[j][k]]>cost[j]+cost[k]) {cost[f[j][k]]=cost[j]+cost[k];ans[f[j][k]]=ans[j]*ans[k];}
else if (cost[f[j][k]]==cost[j]+cost[k]) ans[f[j][k]]+=ans[j]*ans[k];
}
}
}
void input()
{
cin >> n;
for(int i=0;i<n;i++)
cin >> cost[i];
long a,b,c;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=-1;
while(scanf("%ld %ld %ld",&a,&b,&c)!=EOF)
{
f[a][b]=c;
f[b][a]=c;
}
for(int i=0;i<n;i++)
ans[i]=1;
}
void output()
{
cout << cost[0] << " " << ans[0];
}
int main()
{
input();
dijkstra();
output();
return 0;
}