官方题解地址:http://pan.baidu.com/s/1mg5S5z6
A
好神啊= =第一次写01分数规划
其实分数规划是要求$$ Maximize/Minimize \ \ L=\frac{A(x)}{B(x)}$$
这里我们拿最大来举例吧……因为本题就是最大嘛~
通用解法是:二分= =
假设最优解为$\lambda^*$,那么有$$\lambda^*=f(x^*)=\frac{A(x^*)}{B(x^*)} \\ \Rightarrow \lambda^* *B(x^*)=A(x^*) \\ \Rightarrow 0=A(x^*)-\lambda^* *B(x^*) $$
那么我们由上面的形式构造一个新函数$g(\lambda)$:$$g(\lambda)=max_{x \in S} \{A(x)-\lambda*B(x)\}$$
这个函数是有单调性的……我就不证明了……重点是后面的部分:
$$ \lambda<\lambda^* \Rightarrow \lambda<\frac{A(x^*)}{B(x^*)} \Rightarrow A(x^*)-\lambda *B(x^*) >0 \\ \lambda>\lambda^* \Rightarrow \lambda>\frac{A(x^*)}{B(x^*)} \Rightarrow A(x^*)-\lambda *B(x^*) <0 $$
所以我们得到这样一个定理(重要):$$\begin{cases} g( \lambda )=0 & \Leftrightarrow & \lambda = \lambda^* \\ g( \lambda )<0 & \Leftrightarrow & \lambda < \lambda^* \\ g( \lambda )>0 & \Leftrightarrow & \lambda > \lambda^* \end{cases} $$
这题里面,$A(x)$就是边权和,$\lambda*B(x)$就是选这些点的代价啦。
那么很明显对于一个已知的$\lambda$(收益比),我们可以用最大权闭合图的模型来求出$g(\lambda)$,(因为是最大!)从而得知我们二分的这个答案$\lambda$是偏大还是偏小了……
这玩意是不是叫点边均带权的最大密度子图啊QAQ
然而这题有个细节要注意,因为我们用的是最大权闭合图,这里的边权又比较特殊……所以不会出现$g(\lambda)<0$的情况,(那种时候会变成=0),所以我们应该是在>0的时候令L=mid;else R=mid;
而我一开始是反过来写的……所以就跪了TAT 后来我灵(nao)机(zi)一(yi)动(chou),将ans<0改成了ans<eps……这就将<=0的情况都包括进去了→_→顺利出解。
事实上考试时我这题只有暴力分= =因为我数组开!小!了!……一开始定数组大小的时候没想出来正解……所以是直接按点数和边数开的……但是如果是网络流的话,点数应该是$n+m$,边数会是$n+3m$……(没算源汇)QAQTAT
1 //Round4 A 2 #include<cstdio> 3 #include<cmath> 4 #include<queue> 5 #include<cstring> 6 #include<cstdlib> 7 #include<iostream> 8 #include<algorithm> 9 #define rep(i,n) for(int i=0;i<n;++i) 10 #define F(i,j,n) for(int i=j;i<=n;++i) 11 #define D(i,j,n) for(int i=j;i>=n;--i) 12 #define pb push_back 13 using namespace std; 14 typedef long long LL; 15 inline int getint(){ 16 int r=1,v=0; char ch=getchar(); 17 for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1; 18 for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch; 19 return r*v; 20 } 21 const int N=10010,M=40010; 22 const double eps=1e-6,INF=1e10; 23 /*******************template********************/ 24 25 int n,m,u[M],v[M]; 26 double p[M],ans,c[N]; 27 struct edge{int to;double v;}; 28 struct Net{ 29 edge E[M]; 30 int head[N],nxt[M<<1],cnt; 31 void ins(int x,int y,double v){ 32 E[++cnt]=(edge){y,v}; nxt[cnt]=head[x]; head[x]=cnt; 33 } 34 void add(int x,int y,double v){ 35 ins(x,y,v); ins(y,x,0); 36 } 37 int S,T,cur[N],d[N]; 38 queue<int>Q; 39 bool mklevel(){ 40 memset(d,-1,sizeof d); 41 d[S]=0; 42 Q.push(S); 43 while(!Q.empty()){ 44 int x=Q.front(); Q.pop(); 45 for(int i=head[x];i;i=nxt[i]) 46 if (d[E[i].to]==-1 && E[i].v>0){ 47 d[E[i].to]=d[x]+1; 48 Q.push(E[i].to); 49 } 50 } 51 return d[T]!=-1; 52 } 53 double dfs(int x,double a){ 54 if (x==T) return a; 55 double flow=0.0; 56 for(int &i=cur[x];i && a-flow>eps;i=nxt[i]){ 57 if (d[E[i].to]==d[x]+1 && E[i].v>eps){ 58 double f=dfs(E[i].to,min(a-flow,E[i].v)); 59 E[i].v-=f; 60 E[i^1].v+=f; 61 flow+=f; 62 } 63 } 64 if (fabs(flow)<eps) d[x]=-1; 65 return flow; 66 } 67 void Dinic(){ 68 while(mklevel()){ 69 F(i,0,T) cur[i]=head[i]; 70 ans-=dfs(S,INF); 71 } 72 } 73 void build(double x){ 74 // cout <<"mid="<<x<<endl; 75 cnt=1; memset(head,0,sizeof head); 76 S=0,T=n+m+1; ans=0.0; 77 F(i,1,n) add(S,i,(double)c[i]*x); 78 F(i,1,m){ 79 ans+=p[i]; 80 add(u[i],i+n,INF); 81 add(v[i],i+n,INF); 82 add(i+n,T,p[i]); 83 } 84 } 85 void init(){ 86 n=getint(); m=getint(); 87 F(i,1,n) c[i]=getint(); 88 F(i,1,m){ 89 u[i]=getint(); v[i]=getint(); p[i]=getint(); 90 } 91 double l=0,r=1e9,mid; 92 while(r-l>eps){ 93 mid=(l+r)/2; 94 build(mid); 95 Dinic(); 96 // printf("mid=%f ans=%f\n",mid,ans); 97 if (ans<eps) r=mid; 98 else l=mid; 99 } 100 printf("%.2f\n",l); 101 } 102 }G1; 103 104 int main(){ 105 #ifndef ONLINE_JUDGE 106 freopen("A.in","r",stdin); 107 freopen("A.out","w",stdout); 108 #endif 109 G1.init(); 110 return 0; 111 }