P1875 佳佳的魔法药水
题目背景
发完了 k 张照片,佳佳却得到了一个坏消息:他的 MM 得病了!佳佳和大家一样焦急万分!治好 MM 的病只有一种办法,那就是传说中的 0 号药水......怎么样才能得到 0 号药水呢?你要知道佳佳的家境也不是很好,成本得足够低才行......
题目描述
得到一种药水有两种方法:可以按照魔法书上的指导自己配置,也可以到魔法商店里去买------那里对于每种药水都有供应,虽然有可能价格很贵。在魔法书上有很多这样的记载:
1 份 A 药水混合 1 份 B 药水就可以得到 1 份 C 药水。(至于为什么 1+1=1,因为......这是魔法世界)好了,现在你知道了需要得到某种药水,还知道所有可能涉及到的药水的价格以及魔法书上所有的配置方法,现在要问的就是:
- 最少花多少钱可以配制成功这种珍贵的药水;
- 共有多少种不同的花费最少的方案(两种可行的配置方案如果有任何一个步骤不同则视为不同的)。假定初始时你手中并没有任何可以用的药水。
输入格式
第一行有一个整数 N(1≤N≤1000),表示一共涉及到的药水总数。药水从 0∼N−1 顺序编号,0 号药水就是最终要配制的药水。
第二行有 N 个整数,分别表示从 0∼N−1 顺序编号的所有药水在魔法商店的价格(都表示 1 份的价格)。
第三行开始,每行有三个整数 A、B、C,表示 1 份 A 药水混合 1 份 B 药水就可以得到 1 份 C 药水。注意,某两种特定的药水搭配如果能配成新药水的话,那么结果是唯一的。也就是说不会出现某两行的 A、B 相同但 C 不同的情况。
输入以一个空行结束。
输出格式
输出两个用空格隔开的整数,分别表示得到 0 号药水的最小花费以及花费最少的方案的个数。
保证方案数不超过 263−1。
输入输出样例
输入 #1复制
7
10 5 6 3 2 2 3
1 2 0
4 5 1
3 6 2
输出 #1复制
10 3
说明/提示
数据范围:
每一种药水的价格均在 [1,2.8×104] 范围内。
样例说明:
最优方案有 3 种,分别是:
直接买 0 号药水;买 4 号药水、5 号药水配制成 1 号药水,直接买 2 号药水,然后配制成 0 号药水;买 4 号药水、5 号药水配制成 1 号药水,买 3 号药水、6 号药水配制成 2 号药水,然后配制成 0 号药水。
实现代码:
cpp
#include <cstdio>
#include <iostream>
using namespace std;
int cost[9999],ans[9999];
int soc[3000][3000];
bool f[3000];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&cost[i]),ans[i]=1;
int a,b,c;
while(scanf("%d%d%d",&a,&b,&c)!=EOF)
soc[a+1][b+1]=soc[b+1][a+1]=c+1;
for(int i=1;i<n;i++)
{
int maxn=0x7fffffff;
for(int j=1;j<=n;j++)
if(!f[j]&&cost[j]<maxn)
b=j,maxn=cost[j];
f[b]=1;
for(int j=1;j<=n;j++)
if(f[j]&&soc[b][j])
{
if(cost[b]+cost[j]==cost[soc[b][j]])
ans[soc[b][j]]+=ans[b]*ans[j];
if(cost[b]+cost[j]<cost[soc[b][j]])
cost[soc[b][j]]=cost[b]+cost[j],ans[soc[b][j]]=ans[b]*ans[j];
}
}
printf("%d %d",cost[1],ans[1]);
return 0;
}
P1462 通往奥格瑞玛的道路
题目背景
在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量。
有一天他醒来后发现自己居然到了联盟的主城暴风城。
在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛。
题目描述
在艾泽拉斯,有 n 个城市。编号为 1,2,3,...,n。
城市之间有 m 条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。
每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。
假设 1 为暴风城,n 为奥格瑞玛,而他的血量最多为 b,出发时他的血量是满的。如果他的血量降低至负数,则他就无法到达奥格瑞玛。
歪嘴哦不希望花很多钱,他想知道,在所有可以到达奥格瑞玛的道路中,对于每条道路所经过的城市单次收费的最大值,其最小值为多少。
输入格式
第一行 3 个正整数,n,m,b。分别表示有 n 个城市,m 条公路,歪嘴哦的血量为 b。
接下来有 n 行,每行 1 个非负整数,fi。表示经过城市 i,需要交费 fi 元。
再接下来有 m 行,每行 3 个正整数,ai,bi,ci(1≤ai,bi≤n)。表示城市 ai 和城市 bi 之间有一条公路,如果从城市 ai 到城市 bi,或者从城市 bi 到城市 ai,会损失 ci 的血量。
输出格式
仅一个整数,表示歪嘴哦经过城市单次交费最大值的最小值。
如果他无法到达奥格瑞玛,输出 AFK。
输入输出样例
输入 #1复制
4 4 8
8
5
6
10
2 1 2
2 4 1
1 3 4
3 4 3
输出 #1复制
10
说明/提示
对于 60% 的数据,满足 n≤200,m≤104,b≤200;
对于 100% 的数据,满足 1≤n≤104,1≤m≤5×104,1≤b≤109;
对于 100% 的数据,满足 1≤ci≤109,0≤fi≤109,可能有两条边连接着相同的城市。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
#define ong long long
const int N=10010;
const int M=100010;
const ong inf=LLONG_MAX/3;
int n,m,b,u,v;
int l,r,mid;
ong wi;
struct node{
int a;
ong dis;
};
bool operator < (node x,node y){
return x.dis>y.dis;
} priority_queue<node> q;
int top,g[N],f[N];
ong dis[N];
bool vis[N];
struct edge{
int adj,nex;
ong w;
}e[M];
void add(int x,int y,ong wor){
e[++top]=(edge){y,g[x],wor};
g[x]=top;
} void Dijkstra(int maxn){
for(int i=1;i<=n;i++){
dis[i]=inf;
vis[i]=0;
} dis[1]=0;
while(!q.empty()) q.pop();
q.push((node){1,dis[1]});
while(!q.empty()){
node now=q.top(); q.pop();
int x=now.a;
if(vis[x]) continue;
vis[x]=1;
for(int i=g[x];i;i=e[i].nex){
int p=e[i].adj;
if(f[p]>maxn) continue;
if(dis[x]+e[i].w<dis[p]){
dis[p]=dis[x]+e[i].w;
q.push((node){p,dis[p]});
}
}
}
} int main(){
scanf("%d%d%d",&n,&m,&b);
for(int i=1;i<=n;i++){
scanf("%d",f+i);
r=max(r,f[i]);
} l=max(f[1],f[n]);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&wi);
add(u,v,wi);
add(v,u,wi);
} while(l<r){
mid=(l+r)>>1;
Dijkstra(mid);
if(dis[n]>b)
l=mid+1;
else r=mid;
} Dijkstra(l);
if(dis[n]>b) printf("AFK\n");
else printf("%d\n",l);
return 0;
}
P1073 [NOIP 2009 提高组] 最优贸易
题目背景
本题原题数据极弱,Subtask 0 中的测试点为原题测试点,Subtask 1 中的测试点为 Hack 数据。
题目描述
C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为 1 条。
C 国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙来到 C 国旅游。当他得知同一种商品在不同城市的价格可能会不同这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。设 C 国 n 个城市的标号从 1∼n,阿龙决定从 1 号城市出发,并最终在 n 号城市结束自己的旅行。在旅游的过程中,任何城市可以重复经过多次,但不要求经过所有 n 个城市。阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品――水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。由于阿龙主要是来 C 国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
假设 C 国有 5 个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路为单向通行,双向箭头表示这条道路为双向通行。

假设 1∼n 号城市的水晶球价格分别为 4,3,5,6,1。
阿龙可以选择如下一条线路:1→2→3→5,并在 2 号城市以 3 的价格买入水晶球,在 3 号城市以 5 的价格卖出水晶球,赚取的旅费数为 2。
阿龙也可以选择如下一条线路:1→4→5→4→5,并在第 1 次到达 5 号城市时以 1 的价格买入水晶球,在第 2 次到达 4 号城市时以 6 的价格卖出水晶球,赚取的旅费数为 5。
现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。
输入格式
第一行包含 2 个正整数 n 和 m,中间用一个空格隔开,分别表示城市的数目和道路的数目。
第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。
接下来 m 行,每行有 3 个正整数 x,y,z,每两个整数之间用一个空格隔开。如果 z=1,表示这条道路是城市 x 到城市 y 的单向道路;如果 z=2,表示这条道路为城市 x 和城市 y 之间的双向道路。
输出格式
一个整数,表示最多能赚取的旅费。如果没有进行贸易,则输出 0。
输入输出样例
输入 #1复制
5 5
4 3 5 6 1
1 2 1
1 4 1
2 3 2
3 5 1
4 5 2
输出 #1复制
5
说明/提示
【数据范围】
输入数据保证 1 号城市可以到达 n 号城市。
对于 10% 的数据,1≤n≤6。
对于 30% 的数据,1≤n≤100。
对于 50% 的数据,不存在一条旅游路线,可以从一个城市出发,再回到这个城市。
对于 100% 的数据,1≤n≤100000,1≤m≤500000,1≤x,y≤n,1≤z≤2,1≤ 各城市的编号 ≤n。
水晶球价格 ≤100。
NOIP 2009 提高组 第三题
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, m, d[maxn*3], inq[maxn*3];
vector<pair<int, int>> G[maxn*3];
#define t(x,i) (x+i*n)
#define add(x, y) G[t(x,0)].push_back({t(y,0), 0}), G[t(x,1)].push_back({t(y,1),0}), G[t(x,2)].push_back({t(y,2),0})
void spfa(int s) {
for(int i = 1;i <= n*3;i++) d[i] = INT_MIN;
d[s] = 0;
queue<int> Q; inq[s] = true; Q.push(s);
while(!Q.empty()) {
int x = Q.front(); Q.pop(); inq[x] = false;
for(auto [v, len] : G[x])
if(d[v] < d[x] + len) {
d[v] = d[x] + len;
if(!inq[v]) { Q.push(v); inq[v] = true; }
}
}
}
int main() {
ios_base::sync_with_stdio(0); cin.tie(0);
cin >> n >> m;
for(int i = 1, v;i <= n; ++i) {
cin >> v;
G[t(i,0)].push_back({t(i,1), -v});
G[t(i,1)].push_back({t(i,2), v});
}
for(int i = 1,x,y,z;i <= m; ++i) {
cin >> x >> y >> z; add(x, y);
if(z == 2) add(y, x);
}
spfa(t(1,0));
cout << d[t(n,2)] << endl;
return 0;
}