[01分数规划]分数规划,最优比率生成树,最优比率生成环

分数规划:

有n种物品,每种有v和c两种属性,选出m种使得(Σvi)/(Σci)最大。


Solution:

设最终答案为R,x[i]为0或1,则 Σ(v[i]*x[i]) / Σ(c[i]*x[i]) = R

即 Σ(v[i]*x[i]) - Σ(c[i]*x[i]*R) =0

即 Σ[(v[i]*x[i]) - (c[i]*x[i]*R)] =0

设 f(R)= Σ[(v[i]- c[i]*R)*x[i] ]


二分R,求f(R)=0

当f(R)<0时,R偏大,当f(R)>0时,R偏小

计算每一对 v[i]- (c[i]*R),排序求前m大的和即为f(R) 。

#include<iostream>    
#include<cstdio>    
#include<cmath>    
#include<algorithm>    
using namespace std;   
const double eps=1e-7;  
int n,m,ansv,anst;  
struct node    
{    
  int v,t;    
}a[210];  
  
int main()    
{
  scanf("%d %d",&n,&m); m=n-m;   
  for(int i=0;i<n;++i)scanf("%d",&a[i].v);  
  for(int i=0;i<n;++i)scanf("%d",&a[i].t);  
  
  double L=0.0,R=1000.0;  
  double t[210];  
  double mid;
  while(L<R+eps)  
  {  
    mid=(L+R)/2;  
    for(int i=0;i<n;++i)  
        t[i]=a[i].v-mid*a[i].t;  
    sort(t,t+n);  
    double s=0.0;  
    for(int i=m;i<n;++i)s+=t[i];  
    if(s>0)L=mid+eps;  
    else R=mid-eps;  
  }  
  printf("%.3lf",mid);  
  return 0;    
}    


最优比率生成树:

一场地震把约翰家的牧场摧毁了,约翰决心重建家园。约翰已经重建了N个牧场,现在他希望能修建一些道路把它们连接起来。

研究地形之后,约翰发现可供修建的道路有M条。

碰巧的是,奶牛们最近也成立一个工程队,专门从事修复道路。而然,奶牛们很有经济头脑,如果无利可图,它们是不会干的。

奶牛们关注的是挣钱速度,即总利润和总施工时间的比值。

约翰和奶牛达成了协议,奶牛负责修建道路,将所有牧场连通,而约翰需要支付F元。

每条道路都有自己的施工时间和建造成本。连接两个相同的牧场的道路可能有多条。

保证所有的牧场必定是可连通的,不过也有可能一些道路的建造成本之和会超过F。

请帮助奶牛们选择修复哪些道路,才能使单位时间的利润最大?


Solution:

R=[ F - Σ(c[i]*x[i]) ]/ Σ(t[i]*x[i]) 

f(R)=F - Σ[ (c[i]+t[i]*R)*x[i] ]

二分R使  Σ[ (c[i]+t[i]*R)*x[i] ] - F=0 得到最优答案


#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const double Eps=1e-6;
int n,m,q,f[5010];
double res;

struct Road
{
	int u,v;
	int c,t;
	bool operator <(const Road &x)const
	{return c+res*t<x.c+res*x.t;}
}r[10010];

int find(int x)
{
	return x==f[x]?x:f[x]=find(f[x]);
}

double Solve()
{
	double Tmp=0;
	int c=0;
	sort(r+1,r+1+m);
	for(int i=1;i<=n;++i)f[i]=i;
	for(int i=1;i<=m;++i)
	{
		int x=find(r[i].u);
		int y=find(r[i].v);
		if(x!=y)
		{
			f[y]=x;
			Tmp+=r[i].c+res*r[i].t;
			if(c==n-1)return Tmp-q;
		}
	}
	return Tmp-q;
}

int main()
{
	scanf("%d %d %d",&n,&m,&q);
	for(int i=1;i<=m;++i)
	scanf("%d %d %d %d",&r[i].u,&r[i].v,&r[i].c,&r[i].t);
	double L=0,R=100000;
	while(R-L>Eps)
	{
		res=(L+R)/2;
		double Tmp=Solve();
		if(Tmp>0)R=res-Eps;
		else L=res+Eps;
	}
	printf("%.4lf\n",res);
	return 0;
}

最优比率生成环:

作为对奶牛辛勤工作的回报,约翰决定带她们去附近的大城市玩一天。

这个城市有L个景点,参观第i个景点会给奶牛带来Fi点欢乐度。

第二天一早,奶牛可以自由选择从一个景点出发,约翰会负责开车把她们送到那里,但她们晚上必须回到这个景点和约翰汇合。

大城市里都是单行道,第i条道路从第L1i个建筑通向第L2i个建筑,走完需要Ti的时间。

奶牛讨厌走路,定义一条游览线路的“欢乐指数”为参观这条线路上所有景点的欢乐度之和与花在路上的时间之和的比值。

欢乐指数越大的线路越受欢迎。当然,参观同一景点两次不会带来双倍的欢乐。

假设奶牛们至少参观两个景点,请帮她们找到一条欢乐指数最大的线路。


Solution:

Spfa验证是否存在正环

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const double Eps=1e-6;
int n,m,cnt;
double Dis[1010],res;
bool Vis[1010];
int Num[1010],h[1010];

struct Node
{
	int to;
	double t;
	Node *nxt;
}G[1010],Poor[10010];

inline void Push(int u,int v,int c)
{
	Node *t=&Poor[++cnt];
	t->to=v;
	t->t=c;
	t->nxt=G[u].nxt;
	G[u].nxt=t;
}

bool Spfa(double x)
{
	queue<int> Q;
	for(int i=1;i<=n;++i)
	Dis[i]=0,Num[i]=Vis[i]=1,Q.push(i);
	while(!Q.empty())
	{
		int u=Q.front();
		Q.pop();
		for(Node *i=G[u].nxt;i;i=i->nxt)
		{
			int v=i->to;
			double w=(double)h[u]-x*(i->t);
			if(Dis[v]<Dis[u]+w)
			{
				Dis[v]=Dis[u]+w;
				if(!Vis[v])
				{
					Vis[v]=true;
					Q.push(v);
					Num[v]++;
					if(Num[v]>n)return true;
				}
			}
		}
		Vis[u]=false;
	}
	return false;
}

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i)
	scanf("%d",&h[i]);
	for(int i=1;i<=m;++i)
	{
		int a,b,c;
		scanf("%d %d %d",&a,&b,&c);
		Push(a,b,c);
	}
	double L=0,R=10000;
	while(R-L>Eps)
	{
		double M=(L+R)/2;
		if(Spfa(M))
		{
			res=M;
			L=M+Eps;
		}
		else R=M-Eps;
	}
	printf("%.2lf\n",res);
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值