[JSOI2008] 小店购物
Posted init-神眷の樱花
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[JSOI2008] 小店购物相关的知识,希望对你有一定的参考价值。
题面
题解
这道题建边方式很套路。
定义有向边 \\((u,v,w)\\) 表示买了 \\(u\\) 之后,能以边权 \\(w\\) 的价格买 \\(v\\) 。
对于原价的物品,建一个超级源点,分别连接每个物品。对于特价的物品,根据以上的定义,连接两个有依赖关系的物品。
因为通过这样的定义方式,每个物品 \\(v\\) 能被买,当且仅当 \\(u\\) 被买,为了花费最小,所以我们肯定要选取其中边权最小的边。
所以最后就会发现,要买完所有的物品,其实就是求这幅有向图的最小树形图。
根据贪心的思想,因为每个物品都要买 (对于不用买的物品,我们可以看做是以 \\(0\\) 的价格买入),而以能以它的最低价格买就以它的最低价格买肯定是最优的 (即 \\(u\\) 是要买的),所以我们在满足这个条件的前提下,记录最小的价格,先提前算出买 \\(tot_i - 1\\) 个物品的价格,然后跑最小树形图算出买剩余的一个的价格。
最小树形图自然就是用朱刘算法啦。
小细节
- 对于买 \\(u\\) 优惠 \\(v\\) 而 \\(u\\) 不用买的情况,边权应该为无穷大。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF = 1e9;
const int N = 105;
const int M = N * N + 5;
double in[N],val[N],w;
int pre[N],tot[N],vis[N],id[N];
struct edge{
int from,to;
double dis;
}a[M];
double work(int n,int m,int root) {
double ans = 0;
while(true){
for(int i = 1;i <= n; i++) in[i] = INF;
for(int i = 1;i <= m; i++){
int u = a[i].from,v = a[i].to;
if(u != v && a[i].dis < in[v])
in[v] = a[i].dis,pre[v] = u;
}
int cnt = 0;
memset(vis,0,sizeof(vis));
memset(id,0,sizeof(id));
for(int i = 1;i <= n;i++){
if(i == root)continue;
ans += in[i];
int v = i;
while(vis[v] != i && !id[v] && v != root)
vis[v] = i,v = pre[v];
if(!id[v] && v != root){
id[v] = ++cnt;
for(int u = pre[v];u != v;u = pre[u])
id[u] = cnt;
}
}
if(cnt == 0) break;
for(int i = 1;i <= n;i++)
if(!id[i]) id[i] = ++cnt;
for(int i = 1;i <= m;i++){
int u = a[i].from,v = a[i].to;
a[i].from = id[u],a[i].to = id[v];
if(id[u] != id[v]) a[i].dis -= in[v];
}
root = id[root];
n = cnt;
}
return ans;
}
int main(){
int n,m;
scanf("%d",&n);
for(int i = 1; i <= n; i++) {
scanf("%lf%d",&val[i],&tot[i]);
a[i].from = n + 1;
a[i].to = i; a[i].dis = tot[i] == 0 ? 0 : val[i];
}
scanf("%d",&m);
for(int i = 1,u,v; i <= m; i++) {
scanf("%d%d%lf",&u,&v,&w);
if(tot[u] == 0) {
a[n + i].from = u;
a[n + i].to = v; a[n + i].dis = INF;
}
else if(tot[v] == 0) {
a[n + i].from = u;
a[n + i].to = v; a[n + i].dis = 0;
}
else {
a[n + i].from = u;
a[n + i].to = v; a[n + i].dis = w;
val[v] = min(val[v],w);
}
} double res = 0;
for(int i = 1; i <= n; i++)
if(tot[i]) res += (tot[i] - 1) * val[i];
res += work(n + 1,n + m,n + 1);
printf("%.2lf\\n",res);
return 0;
}
以上是关于[JSOI2008] 小店购物的主要内容,如果未能解决你的问题,请参考以下文章
LuoguP2792 [JSOI2008]小店购物(最小树形图)