QBXT 武器

QBXT 武器

Description

  • 一共有n种武器,直接购买成本为ai。有m种合成方法,可以将x,y两种各一个武器合成一个z武器,请问获得第n种武器的最小成本和获得最小成本的方案数。

Input

  • 输入的第一行包含两个正整数n,m表示武器数量和合成方法数量。

    接下来一行包含n个正整数,其中ai 表示第i件物品的成本。

    接下来m行每行三个正整数x, y , z,表示可以将一个x, y合为一个z。

    最后一行输入一个整数type, 如果type=0输出最小成本;如果type=1则输出最小成本 和获得最小成本的方案数。

Output

  • 如果type=0输出一个整数,表示获得第n种武器的最小成本。

    如果type=1输出两个整数,分别表示获得第n种武器的最小成本和获得最小成本的方案数。

Sample Input

7 3

5 6 3 2 2 3 10

1 2 7

4 5 1

3 6 2

1

Sample Output

10
3

Data Size

  • 对于20%的数据,保证n<=10 m<=20;

    对于60%的数据,保证n<=500 m<=1000;

    对于100%的数据,保证n<=50000 m<=100000; ai<=200000000;

    对于每部分的数据,有一半type=0,一半type=1。

题解:

  • 考虑暴力。可以设dp[i]表示只用前i个武器获得第n个武器的最小代价。然后每加入一个物品,就滚动去用m条规则进行松弛操作。最终得到第n个武器的最小代价。这种做法求不出方案数。可以暴力得到30pts。

  • 考虑正解。最短路计数。

  • 官方题解:

  • 做法:我们可以用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]更新为ans[j] * ans[k];

    如果cost[j]+cost[k]=cost[f[j][k]],则将ans[f[j][k]]加上ans[j]*ans[k]。

    更新完所有和k号药水有关的配方之后,将v[k]赋值为真,

    重复上述过程直到所有药水都更新过为止。

    最后输出cost[0]和ans[0]即可。

#include <iostream>
#include <cstdio>
#include <queue>
#define N 50005
#define M 100005
#define int long long
using namespace std;

struct Node
{
    int val, pos;
    friend bool operator < (Node x, Node y) {
        return x.val > y.val;
    }
};
priority_queue<Node> que;
struct E {int next, to, p;} e[M * 2];
int n, m, num, tag;
int dis[N], h[N], tot[N];
bool vis[N];

int read()
{
    int x = 0; char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
    return x;
}

void add(int u, int v, int p)
{
    e[++num].next = h[u];
    e[num].to = v;
    e[num].p = p;
    h[u] = num;
}

signed main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        dis[i] = read();
        que.push((Node){dis[i], i});
        tot[i] = 1;
    }
    for(int i = 1; i <= m; i++)
    {
        int x = read(), y = read(), z = read();
        add(x, z, y);
        if(x != y) add(y, z, x);
    }
    cin >> tag;
    while(que.size())
    {
        int pos = que.top().pos, val = que.top().val;
        que.pop();
        vis[pos] = 1;
        for(int i = h[pos]; i != 0; i = e[i].next)
            if(vis[e[i].p])
            {
                if(dis[pos] + dis[e[i].p] < dis[e[i].to])
                    dis[e[i].to] = dis[pos] + dis[e[i].p],
                    tot[e[i].to] = tot[pos] * tot[e[i].p];
                else if(dis[pos] + dis[e[i].p] == dis[e[i].to])
                    tot[e[i].to] += tot[pos] * tot[e[i].p];
            }
    }
    cout << dis[n] << ' ';
    if(tag) cout << tot[n];
    return 0;
}

转载于:https://www.cnblogs.com/BigYellowDog/p/11625658.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值