洛谷题单指南-最短路-P1875 佳佳的魔法药水
原题链接:https://www.luogu.com.cn/problem/P1875
题意解读:n种药水,每个药水都有原始价格,又给出若干药水合成的方案1份a+1份b合成1份c,c的价格可以是a+b的价格之和,求药水0的最低价格,以及获得最低价格的方案数。
解题思路:
1、朴素想法
这明显是一个DP问题,设dist[i]表示药水i的最低价格,cnt[i]表示药水i获得最低价格的方案数,初始时dist[i]药水i的原始价格,cnt[i]=1。
对于每种合成方案,可以得到
if(dist[c] > dist[a] + dist[b])
{
dist[c] = dist[a] + dist[b];
cnt[c] = cnt[a] * cnt[b];
}
else if(dist[c] == dist[a] + dist[b])
{
cnt[c] += cnt[a] * cnt[b];
}
于是准备找到某种拓扑序,不断枚举到合成方案,进行合成更新。
但是!由于这里的合成关系是有可能形成环的,不存在严格拓扑序,因此无法迭代DP解决,可以借助Dijikstra的思想来获得拓扑序!!
2、Dijikstra思想
从Dijikstra的本质出发:不断选取未确定最低价格的药水a(重复n次),然后在已确定最低价格的药水中找到一个能和a形成合成方案的b,再去共同更新合成后药水c的最低价格以及方案数。
下面根据这样的思想,对样例数据进行模拟。
初始值:
dist[0]=10 dist[1]=5 dist[2]=6 dist[3]=3 dist[4]=2 dist[5]=2 dist[6]=3
cnt[0]=1 cnt[1]=1 cnt[2]=1 cnt[3]=1 cnt[4]=1 cnt[5]=1 cnt[6]=1
第0步:
确定药水4的最低价格:2
第1步:
确定药水5的最低价格:2
用药水5和药水4确定药水1的 最低价格dist[1]=4 方案数cnt[1]=1
第2步:
确定药水3的最低价格:3
第3步:
确定药水6的最低价格:3
更新药水2的方案数:cnt[2]+cnt[6]*cnt[3]=2
第4步:
确定药水1的最低价格:4
第5步:
确定药水2的最低价格:6
更新药水0的方案数:cnt[0]+cnt[2]*cnt[1]=3
第6步:
确定药水0的最低价格:10
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int dist[N]; //获得每种药水的最低价格,初始为每种药水的原始价格
long long cnt[N]; //每种药水最低价格的方案
int g[N][N]; //g[a][b]=g[b][a]=c
bool vis[N]; //标记药水是否已更新过最低价格
int n;
int main()
{
memset(g, -1, sizeof(g));
cin >> n;
for(int i = 0; i < n; i++)
{
cin >> dist[i];
cnt[i] = 1;
}
int a, b, c;
while(cin >> a >> b >> c)
{
g[a][b] = g[b][a] = c; //用a和b可以合成c
}
//朴素Dijikstra算法
//重复n次
for(int i = 0; i < n; i++)
{
//找到未确定最低价格的价格最低的药水
int a = -1, minp = 2e9;
for(int j = 0; j < n; j++)
{
if(!vis[j] && (a == -1 || minp > dist[j]))
{
a = j;
minp = dist[j];
}
}
vis[a] = true; //标记a已确定最低价格
//找到与a配对的已确定过最低价格的b,更新共同的c的最低价格
for(int b = 0; b < n; b++)
{
if(vis[b] && g[a][b] != -1)
{
int c = g[a][b];
if(dist[c] > dist[a] + dist[b])
{
dist[c] = dist[a] + dist[b];
cnt[c] = cnt[a] * cnt[b];
}
else if(dist[c] == dist[a] + dist[b])
{
cnt[c] += cnt[a] * cnt[b];
}
}
}
}
cout << dist[0] << " " << cnt[0];
return 0;
}
浙公网安备 33010602011771号