ACM-ICPC (10/12)
 01分数规划
01分数规划
背景:根据楼教主回忆,曾经在一场比赛中秒掉了一道最优比例生成树问题,导致很多人跟风失败,最终悲剧。
- 
什么是01分数规划呢? 
 这样的等式求最大,最小即为01分数规划。
这样的等式求最大,最小即为01分数规划。  
如果你不知道该如何去解,你可能会去贪心,DP去做,但是这样是很复杂的。
- 
解法:二分,迭代(计算几何大佬都知道这种方案,但是我不是) 
- 
直接二分ans,   根据符号二分转移。 
例题一:pku 2796
 
题意:  最大。
最大。
#include <stdio.h> #include <algorithm>  using namespace std;  const int maxn = 1005;  int n,k; double a[maxn],b[maxn]; double c[maxn];  bool cmp(double a,double b) { return a > b; }  bool calc(double x) { for(int i = 0; i < n; i++) c[i] = a[i] - x*b[i]; sort(c,c+n,cmp);  double sum = 0; for(int i = 0; i < n-k; i++) sum +=c[i]; if(sum>=0) return true; return false;  }  int main() { //freopen("in.txt","r",stdin); while(scanf("%d%d",&n,&k),n) {  for(int i = 0; i < n; i++) scanf("%lf",&a[i]); for(int i = 0; i < n; i++) scanf("%lf",&b[i]);  double l = 0,r = 1;  while(r-l>1e-6) { double mid = (l + r)/2;  if(calc(mid)) l = mid; else r = mid;  } printf("%.0lf\n",l*100);  } return 0; }
例题二:pku 2728 最优比例生成树

题意:给定n 个点,坐标(x,y,z),n条无向边的图,国王将这n个点连起来(生成树),建一条边有花费, 求单位最小花费最小比例。
同理:二分这个比例,边权为 ,最小生成树 ans >= 0,说明 x过小,二分转移 l = mid;
#include <stdio.h> #include <string.h> #include <math.h> #include <vector> #include <queue> #include <map> #include <set> #include <iostream> #include <algorithm>  using namespace std;  const int maxn = 1005;  double maps[maxn][maxn]; bool vis[maxn]; double dis[maxn];  int n;  double Prim() { memset(vis,false,sizeof(vis)); for(int i = 1; i<= n; i++) dis[i] = 1000000000;  double ans = 0; dis[1] = 0;  for(int i = 1; i <= n; i++) { double tmp = 1000000000; int k = 0;  for(int j = 1; j <= n; j++) { if(!vis[j]&&dis[j]<tmp) { tmp = dis[j]; k = j; } }  vis[k] = true; ans += tmp;  for(int j = 1; j<= n; j++) { if(!vis[j]&&dis[j]>maps[k][j]) dis[j] = maps[k][j]; }  } return ans; }  struct Node { double x,y,z; }nodes[maxn];  double dist(int i,int j,double x) { double fx = fabs(nodes[i].x-nodes[j].x); double fy = fabs(nodes[i].y-nodes[j].y); double fz = fabs(nodes[i].z-nodes[j].z); return fz - x*sqrt(fx*fx+fy*fy); }  double eps = 1e-4;  int main() { //freopen("in.txt","r",stdin); while(scanf("%d",&n),n) {  for(int i = 1; i <= n; i++) scanf("%lf%lf%lf",&nodes[i].x,&nodes[i].y,&nodes[i].z);  double l = 0,r = 1000000000;  while(r-l>1e-5) { double mid = (r+l)/2;  for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) maps[i][j] = dist(i,j,mid);  double ans = Prim();  if(ans<=0) r = mid; else l = mid; }  printf("%.3f\n",l); }  return 0; }
例题三:pku 3621 最优比例环。(双倍经验题Uva 11090,题意相反)

题意:给定一个L个节点,P条有向边的图,奶牛从一个城市出发,走一个环回到起点,点上有权值,边上也有长度,求单位长度的点权最大。
分析:还是二分 ans,由于是一个环,一条边上,算起点权值就好了。改边权, ,
由于求的是比例最大,这时SPFA,应反向松弛,才能得到最大的比例。
#include <stdio.h> #include <string.h> #include <math.h> #include <string.h> #include <vector> #include <set> #include <map> #include <queue> #include <string> #include <iostream> #include <algorithm>  using namespace std;  const int maxn = 1005;  struct Edge { int from,to; double dist; };  struct BellmanFord { int n, m; vector<Edge> edges; vector<int> G[maxn]; bool inq[maxn]; double d[maxn]; int p[maxn]; int cnt[maxn];  void init(int n) { this->n = n; for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); }  void AddEdge(int from, int to, double dist) { edges.push_back((Edge) { from, to, dist }); m = edges.size(); G[from].push_back(m-1); }  bool negativeCycle() { queue<int> Q; memset(inq, 0, sizeof(inq)); memset(cnt, 0, sizeof(cnt)); for(int i = 0; i < n; i++) { d[i] = 0; inq[0] = true; Q.push(i); }  while(!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = false; for(int i = 0; i < (int)G[u].size(); i++) { Edge& e = edges[G[u][i]]; if(d[e.to] < d[u] + e.dist) //反向松弛 { d[e.to] = d[u] + e.dist; p[e.to] = G[u][i]; if(!inq[e.to]) { Q.push(e.to); inq[e.to] = true; if(++cnt[e.to] > n) return true; } } } } return false; } }sol; int L,P; double f[maxn]; double t[maxn];  vector<Edge> edgestmp;  int main() { //freopen("in.txt","r",stdin); scanf("%d%d",&L,&P);  sol.init(L); for(int i = 0; i < L; i++) scanf("%lf",&f[i]); for(int i = 0; i < P; i++) { int u,v; double dist; scanf("%d%d%lf",&u,&v,&dist); u--;v--; edgestmp.push_back((Edge){u,v,dist}); sol.AddEdge(u,v,dist); }  double l = 0,r = 10000; while(r-l>1e-4) { double mid = (r+l)/2;  sol.init(L);  for(int i = 0; i < P; i++) { int u = edgestmp[i].from; int v = edgestmp[i].to; double dist = edgestmp[i].dist; sol.AddEdge(u,v,f[u]-mid*dist); }  if(sol.negativeCycle()) l = mid; else r = mid; }  printf("%.2f\n",l); return 0; }
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号