洛谷题单指南-最短路-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;
}

 

posted @ 2025-04-10 15:29  hackerchef  阅读(26)  评论(0)    收藏  举报