http://poj.org/problem?id=3621
求一个环的{点权和}除以{边权和},使得那个环在所有环中{点权和}除以{边权和}最大。
0/1整数划分问题
令在一个环里,点权为v[i],对应的边权为e[i],
即要求:∑(i=1,n)v[i]/∑(i=1,n)e[i]最大的环(n为环的点数),
设题目答案为ans,
即对于所有的环都有 ∑(i=1,n)(v[i])/∑(i=1,n)(e[i])<=ans
变形得ans* ∑(i=1,n)(e[i])>=∑(i=1,n)(v[i])
再得 ∑(i=1,n)(ans*e[i]-v[i]) >=0
稍分析一下,就有:
当k<ans时,就存在至少一个环∑(i=1,n)(k*e[i]-v[i])<0,即有负权回路(边权为k*e[i]-v[i]);
当k>=ans时,就对于所有的环∑(i=1,n)(k*e[i]-v[i])>=0,即没有负权回路。
然后我们就可以使新的边权为k*e[i]-v[i],用spfa来判断负权回路,二分ans。
#include <iostream> #include <cstdlib> #include <cstdio> #include <queue> #include <cstring> #include <algorithm> #define RD(x) scanf("%d",&x) #define RD2(x,y) scanf("%d%d",&x,&y) #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define clr0(x) memset(x,0,sizeof(x)) #define clr1(x) memset(x,-1,sizeof(x)) using namespace std; typedef long long LL; const int maxn = 1005,maxm = 105; const double eps = 1e-3; const int inf = 0x7fffffff; int v[maxn]; struct edge{ int v,w,next; edge(){}; edge(int vv,int ww,int nnext):v(vv),w(ww),next(nnext){}; }e[maxn*5]; int head[maxn],vis[maxn],_v[maxn],cnt[maxn],ecnt,n,m; double dist[maxn]; void add(int u,int v,int w) { e[ecnt] = edge(v,w,head[u]); head[u] = ecnt++; } void init() { for(int i = 1;i <= n;++i) RD(_v[i]); ecnt = 0; clr1(head);//判负环的初始化 int u,v,w; while(m--){ RD3(u,v,w); add(u,v,w); } return ; } bool spfa(double mid) { clr0(vis),clr0(cnt); fill(dist,dist + n + 1,inf); dist[1] = 0; queue<int> q; q.push(1); while(!q.empty()){ int u = q.front(); q.pop(); vis[u] = false; cnt[u]++; if(cnt[u] > n) return true; for(int i = head[u];i != -1;i = e[i].next){ int v = e[i].v; double tmp = mid*e[i].w - _v[v];//"边权" if(dist[u] + tmp < dist[v]){ dist[v] = dist[u] + tmp; if(!vis[v]){ vis[v] = true; q.push(v); } } } } return false; } void work() { double l = 0,r = 10000,mid,ans; while(r - l > eps){ mid = (l + r)/2; if(spfa(mid)){ ans = mid; l = mid - 0.000001; }else r = mid + 0.000001; } printf("%.2f\n",ans); } int main() { while(~RD2(n,m)){ init(); work(); } return 0; }