其他分享
首页 > 其他分享> > [JSOI2008] 小店购物

[JSOI2008] 小店购物

作者:互联网

题面

小店购物

题解

这道题建边方式很套路。

定义有向边 \((u,v,w)\) 表示买了 \(u\) 之后,能以边权 \(w\) 的价格买 \(v\) 。

对于原价的物品,建一个超级源点,分别连接每个物品。对于特价的物品,根据以上的定义,连接两个有依赖关系的物品。

因为通过这样的定义方式,每个物品 \(v\) 能被买,当且仅当 \(u\) 被买,为了花费最小,所以我们肯定要选取其中边权最小的边。

所以最后就会发现,要买完所有的物品,其实就是求这幅有向图的最小树形图。

根据贪心的思想,因为每个物品都要买 (对于不用买的物品,我们可以看做是以 \(0\) 的价格买入),而以能以它的最低价格买就以它的最低价格买肯定是最优的 (即 \(u\) 是要买的),所以我们在满足这个条件的前提下,记录最小的价格,先提前算出买 \(tot_i - 1\) 个物品的价格,然后跑最小树形图算出买剩余的一个的价格。

最小树形图自然就是用朱刘算法啦。

小细节

代码

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

标签:const,int,double,JSOI2008,购物,最小,树形图,物品,小店
来源: https://www.cnblogs.com/sjzyh/p/14826188.html