【01分数规划】二分+判圈
作者:互联网
01分数规划一般是用来解决∑t∑f=u,u
最大的问题,这个变形一下可以写成∑f−u∑t=0。这样可以二分u
,如果∑f−u∑t>0,说明u
太小,否则u
太大。
题意
n个点m条边的有向图,每个点有一个权值f
,每条边有个权值t
,现在要从任意一点出发并回到该点,问最大的∑t∑f是多少。
题解
有个结论:简单环的情况是最优的。
根据∑f−u∑t=0,二分u
,将每条边的权值变为f-u*t
,这样如果该图中有正环,说明∑f−u∑t>=0,那么u
还可以再大,否则u
就要缩小。
为什么不根据负环check呢? 因为要求的是u
最大,图里可能既有负环也有正环,那么肯定需要选正环而不是负环。
代码
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
#define x first
#define y second
typedef long long ll;
typedef pair<int,double> pii;
const int maxn = 1000+5;
int n,m;
struct edge{
int u,v;
double w;
};
vector<edge> Edge;
vector<pii> G[maxn];
double f[maxn];
void add(int u, int v,int w) {
G[u].push_back(pii(v,w));
}
double d[maxn];
int inq[maxn],cnt[maxn];
bool spfa(double mid) {
queue<int> que;
for(int i = 1; i <= n; ++i)
d[i] = 10000000;
memset(inq,0,sizeof inq);
memset(cnt,0,sizeof cnt);
int s = 0;
d[s] = 0;
que.push(0);
inq[s] = 1;
cnt[s] = 1;
while(!que.empty()) {
int u = que.front();
que.pop();
inq[u] = false;
// cout << u << endl;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i].x;
double w = G[u][i].y;
w = (f[u]-mid*w);
// cout <<u<<" "<< v << endl;
if(d[v] > d[u]+w) {
d[v] = d[u]+w;
if(!inq[v]) {
inq[v] = true;
que.push(v);
if(++cnt[v] > n)
return true;
}
}
}
}
return false;
}
int main() {
scanf("%d%d", &n,&m);
for(int i = 1; i <= n; ++i) {
scanf("%lf", &f[i]);
G[0].push_back(pii(i,0));
}
int u,v;
double w;
for(int i = 0; i < m; ++i) {
scanf("%d%d%lf", &u,&v,&w);
G[u].push_back(pii(v,w));
}
double l = -1e9, r = 1e9, ans = l;
for(int i = 0; i < 300; ++i) {
double mid = (l+r)/2;
if(spfa(mid)) {
r = mid;
ans = mid;
} else
l = mid;
}
printf("%.2lf\n", l);
return 0;
}
标签:二分,01,判圈,int,double,sum,gt,maxn,权值 来源: https://blog.csdn.net/Link_Ray/article/details/89293281