0|1分数规划

文章介绍了0|1分数规划问题,涉及分子和分母的求和操作,通过二分法和图上的正环求解,利用SPFA算法求得最值。代码展示了如何将点权处理为边权,转化为在图中寻找正环的问题。
摘要由CSDN通过智能技术生成

这种分子上一大坨求和,分母上一大坨求和,然后求最值的问题属于一类特殊的问题。被称为0|1分数规划问题。
也就是 ∑ f i ∗ w i ∑ g i ∗ w i \frac{\sum{f_i} * w_i} {\sum{g_i} *w_i } giwifiwi
找一组 w i ∈ { 0 , 1 } {w_i} \in \{0,1\} wi{0,1}使上面的式子最小
通常的做法就是二分答案
假设我们要求的是最大值,然后二分一个答案mid,然后推式子
∑ f i ∗ w i ∑ g i ∗ w i > m i d \frac{\sum{f_i} * w_i} {\sum{g_i} *w_i }>mid giwifiwi>mid
∑ f i ∗ w i − m i d ∗ ∑ g i ∗ w i > 0 {\sum{f_i} * w_i}-mid*{\sum{g_i} *w_i }>0 fiwimidgiwi>0
w i ∗ ( ∑ f i − m i d ∗ g i ) > 0 w_i *({\sum{f_i}-mid*g_i})>0 wi(fimidgi)>0
那么只要求出不等号左边的式子的最大值就行了。如果最大值比 0 要大,说明 mid 是可行的,否则不可行

针对于这道题目也是
这里既有边权又有点权,可以把点权下放到出边上
然后上面的式子的含义也就变为了在图上求正环。
正环也可以用spfa来求,只需要和负环对称一下,求最短路变成求最长路即可。

#include <bits/stdc++.h> 
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back
#define inf 0x3f3f3f3f*1ll

using namespace std;

void solve()
{
	int n,m;
	cin>>n>>m;
	vector<vector<pii>>g(n+1);
	vector<int>a(n+1);
	rep(i,1,n){
		cin>>a[i];
	}
	rep(i,1,m){
		int u,v,w;
		cin>>u>>v>>w;
		g[u].pb({v,w});
	}	
	auto check=[&](db tar){
		vector<int>inq(n+1,0);
		vector<int>cnt(n+1,0);
		vector<db>d(n+1,0);
		queue<int>q;
		rep(i,1,n){
			q.push(i);
			inq[i]=1;
		}
		while(q.size()){
			auto u=q.front();
			q.pop();
			inq[u]=0;
			for(auto it:g[u]){
				int v=it.x,w=it.y;
				if(d[v]<d[u]+a[u]-w*tar){
					d[v]=d[u]+a[u]-w*tar;
					cnt[v]=cnt[u]+1;
					if(cnt[v]>=n)	return true;
					if(!inq[v]){
						inq[v]=1;
						q.push(v);
					}
				}
			}
		}
		return false;
	};
	db l=0,r=1010;
	while(r-l>1e-5){
		db mid=(l+r)/2;
		if(check(mid))	l=mid;
		else	r=mid;
	}
	//c++默认输出6位精度
	cout<<setiosflags(ios::fixed)<<setprecision(2)<<l<<endl;
}

signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//   	freopen("1.in", "r", stdin);
	int _;
//	cin>>_;
//	while(_--)
	solve();
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值