P1875 佳佳的魔法药水 (最短路,DP)

题目链接


Solution

好题. 一开始一直在想怎么蛇皮建图,但是发现一直蛇不出来...
正解是用类似于 dijkstra 的算法对所有点进行松弛.
对于每个元素记录两个值:

  • \(cost\) 代表它的最小花费.
  • \(ans\) 代表它的方案数.
  • 同时用一个\(f_{i,j}\)记录第\(i\)种和第\(j\)种药水可以合成第\(f_{i,j}\)这种药水.

似乎可以发现我们存合并状态的数组很像临接矩阵?
然后就可以开始蛇了...

我们对于每一瓶药水,其 \(cost\) 初值为其直接买的花费.
\(ans\) 初值为 1.
每一次选择未松弛的价格最小的药水\(u\),然后对于所有的 \(f_{u,i}\) 值进行更新.

\(1.\) 如若 \(cost_{f_{u,i}}>cost_u+cost_i\)
那么 \(cost_{f_{u,i}}=cost_u+cost_i\),同时 \(ans_{f_{u,i}}=ans_u*ans_i\)

\(2.\) 如果 \(cost_{f_{u,i}}=cost_u+cost_i\)
那么 \(ans_{f_{u,i}}=ans_{f_{u,i}}+ans_u*ans_i\)

然后最后的答案即为 \(cost[0]\)以及 \(ans[0]\).

Code

#include<bits/stdc++.h>
using namespace std;
const int inf=192608173;
const int maxn=1008;
int f[maxn][maxn],n;
int cost[maxn],ans[maxn];
 
void dijkstra()
{
  	int v[1010]={0},k,minimum;
  	for(int i=1;i<=n;i++)
  	{
    	minimum=inf;
    	for(int j=0;j<n;j++)
      	if(!v[j]&&cost[j]<minimum)
      	{k=j;minimum=cost[j];}
      	//类似于dij的选边进行松弛.
    	if(minimum==inf) break;
   	 	v[k]=1;
    	for(int j=0;j<n;j++)
      	if(v[j]&&f[j][k]>-1) 
        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];
        	continue;
        }
        else if(cost[f[j][k]]==cost[j]+cost[k]) 
        ans[f[j][k]]+=ans[j]*ans[k];
  	}
}
 
int main()
{
	scanf("%d",&n);
    for(int i=0;i<n;i++)
    scanf("%d",&cost[i]);
  	int a,b,c;
	memset(f,-1,sizeof(f));
 	while(cin>>a>>b>>c)
  	{f[a][b]=c;f[b][a]=c;}//此处建边.
 	for(int i=0;i<n;i++)
    ans[i]=1;
  	dijkstra();
  	cout<<cost[0]<<" "<<ans[0];
  	return 0;
}
posted @ 2018-08-17 15:26  Kevin_naticl  阅读(271)  评论(0编辑  收藏  举报