传送门
题目:给出N个点M条有向边,要求选出一个环,使得这上面 点权和/边权和 最大。
思路:明显的01分数规划,只是这里的判定不是很好搞。我们先按着分数规划的思路来。设
Xi={0,1}
,
Yi={0,1}
,
R=∑Vi∗Xi∑Wj∗Yj
所以有判定函数
F(R)=∑Vi∗Xi−R∗∑Wj∗Yj
所以说当
F(R)<0
时就有
∑Vi∗Xi−R∗∑Wj∗Yj<0
即
R>∑Vi∗Xi∑Wj∗Yj
,
R
应该减小。反之则应该增大。但我们怎么判断
以下是代码
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
#define MAXM 5005
#define MAXN 1005
#define eps 1e-3
struct node
{
int v, w;
node *next;
}Edge[MAXM*2], *Adj[MAXN], *Mcnt = Edge;
void Addedge(int u,int v,int w)
{
node *t = ++Mcnt;
t->v = v;
t->w = w;
t->next = Adj[u];
Adj[u] = t;
}
int n, m, w[MAXN];
double dis[MAXN];
bool inq[MAXN];
int vis[MAXN];
bool spfa(double W)
{
memset(vis, 0, sizeof vis);
memset(inq, 0, sizeof inq);
for(int i = 2; i <= n; i ++)
dis[i] = 1e15;
dis[1] = 0;
queue<int> q;
q.push(1);
inq[1] = 1;
vis[1] ++;
int u, v;
while(!q.empty())
{
u = q.front();
q.pop();
inq[u] = 0;
for(node *p = Adj[u]; p; p = p->next)
{
v = p->v;
if(dis[u] - w[u] + W*p->w < dis[v])
{
dis[v] = dis[u] - w[u] + W*p->w;
if(!inq[v])
{
q.push(v);
inq[v] = 1;
++ vis[v];
if(vis[v] > n) return 1;
}
}
}
}
return 0;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++)
scanf("%d", &w[i]);
int t1, t2, t3;
for(int i = 1; i <= m; i ++)
{
scanf("%d%d%d", &t1, &t2, &t3);
Addedge(t1, t2, t3);
}
double l = 0, r = 10000, mid;
while(l+eps<r)
{
mid = (l+r)/2;
bool f = spfa(mid);
if(f == 1) l = mid;
else r = mid;
}
printf("%.2lf", l);
return 0;
}