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;
}