分治 —— 01 分数规划
【概述】
分數(shù)規(guī)劃的一般形式為:
特別的,當??時,稱為 01 分數(shù)規(guī)劃
簡單來說,就是有一些二元組 (a[i],b[i]),現(xiàn)在從中選擇某些二元組,使得??最大或最小
這一類題通用的解法是利用二分法來解決:
假設 x 為最優(yōu)解,對應的最優(yōu)函數(shù)值為?λ,那么有:
即:
于是可以構造函數(shù)?
當??時,此時 λ 即為最優(yōu)解
也就是說,當某一個值 λ?滿足上述式子的時候,就是要求的值,因此可以直接二分答案:當上述式子 >0,說明答案小了,更改左區(qū)間;當上述式子 <0,說明答案大了,更改右區(qū)間
double left=0,right=1; while(left-right<=EPS) {double mid=(left+right)/2;double G=INF;for(int i=1;i<=n;i++) G=min(G, a[i]-mid*b[i]);if(judge(G))left=mid;elseright=mid; }【Dinkelbach 算法】
對于 01 規(guī)劃問題,除了利用基本的二分答案來解決外,還有一個常用的算法:Dinkelbach 算法
Dinkelbach 算法是一種的迭代算法,其核心思想是:對于一個值 λ ,我們找到其最優(yōu)解 x,然后再用解 x 來代替 λ ,即令?λ=f(x),然后繼續(xù)迭代,直到 λ 的值不再變動,此時即得出最優(yōu)解。
double init=0.5;//初始值可隨意設置 while(fabs(init-res)>EPS) {res=left;//記錄比率for(int i=1;i<=n;i++)//計算函數(shù)G G=min/max(G,a[i]-init*b[i]);double p=0,q=0;for(int i=1;i<=m;i++){//計算新比率p+=a[i];q+=b[i];}init=p/q;//更新比率 }【常見模型】
01 規(guī)劃問題,除了上述的基本模型外,還有以下三種常見的模型
1.最優(yōu)比率生成樹
1)問題:對于給定的帶權無向圖 G,對于圖中的每條邊 edge[i],都有 value[i] 與 cost[i],現(xiàn)在要求一棵生成樹 T,使得?最大化或最小化,這個生成樹 T,即為最優(yōu)比率生成樹
2)解決:如果一條邊 edge[i]∈T,那么 x[i]=1,否則 x[i]=0,因此可以直接套用 01 規(guī)劃分數(shù)模型
二分法:二分答案 mid,對邊重賦值 weight[i]=value[i]-mid*cost[i],由于是生成樹,因此邊的數(shù)量固定為 n-1 條,因此如果要最大化,只要選取前 n-1 大的 weight[i],也即求最大生成樹,如果要最小化,就求最小生成樹,最后按最大生成樹的權值正負性進行二分
Dinkelbach 算法:設置初始值 init,對邊同樣重賦值 weight[i]=value[i]-init*cost[i],對于最大化來說,求最大生成樹,找到其邊集,對其邊集找橫截距當作下一次的答案,進行迭代
2.最優(yōu)比率環(huán)
1)問題:給定一個存在環(huán)的有向圖 G,每個點都有一個權值 value[i],每條邊也有一條權值 cost[i],現(xiàn)在要求一個環(huán) C,使得環(huán)的?最大化或最小化(在一個環(huán)中,點數(shù)與邊數(shù)是相同的),這個環(huán),即為最優(yōu)比率環(huán)
2)解決:
假設答案為 x,那么對于任意一個環(huán)
當要最大化時,有:,化簡得:
即:
同理,當要最小化時,有:
因此可以直接套用 01 規(guī)劃分數(shù)模型
二分法:設當前答案為 mid,當 mid<x 時,至少存在一個環(huán),使得?,即存在負權回路;當 mid>=x 時,不存在負環(huán),因此可在利用 SPFA 求負環(huán)的過程中對邊進行重賦值,從而進行二分,即如果存在負環(huán),即增大下界,若一直不可行,則無解
Dinkelbach 算法:由于使用 Dinkelbach 算法需要在不斷迭代的過程中記錄負環(huán),實現(xiàn)較為復雜,因此一般在該模型中不采用該方法
3.最大密度子圖
`1)問題:給出一個 n 個點 m 條邊的無向圖,現(xiàn)在要選擇一個子圖,使得這個子圖中邊數(shù)與點數(shù)的比例最大化,這個子圖,即為最大密度子圖
2)解決:
設 g 為最大比率,若要找一個最大密度子圖,那么可以參考 01分數(shù)規(guī)劃的基本模型來構造一個函數(shù) h(g)=|E'|-g*|V'|,當 h(g)=0 時,g 即為最優(yōu)值
關于兩種解法的詳細證明參見?胡伯濤的《最小割模型在信息學競賽中的應用》:點擊這里
二分+最大權閉合圖:
可以發(fā)現(xiàn),邊與點具有依賴關系,即邊存在的必要條件是點的存在,那么我們可以將邊看作點,根據(jù) g 的值來構建一個新圖,即:將原圖中所有代表邊的點向他的兩個端點連接一條有向邊
那么可以直觀的看出,如果選擇一條邊,那么這條邊的兩個端點必然也被選擇,也就是一個閉合子圖的模型
因此,在二分 g 的過程中,不斷的建圖來判斷 g 的正負從而調整邊界即可
void makeMap(double g) {memset(head,-1,sizeof(head)); tot=0;for(int i=1;i<=n;i++)//原圖中的點到匯點 addedge(i,T,g); for(int i=0;i<m;i++) { //源點到原圖中的每條邊addedge(S,n+i+1,1.0);//原圖中的每條邊到之間建邊addedge(n+i+1,pastEdge[i].first,INF); addedge(n+i+1,pastEdge[i].second,INF); } }二分+最小割:
設 g 為最大比率,在建圖時從源點到各點連接一條容量為 m?的單向邊,從各點到匯點連接一條容量為 m+2*g-degree[i] 的單向邊,其中 degree[i] 代表第 i 個點的度數(shù),這樣以后對這個圖求最小割,那么?h(g)=(n*m-maxFlow)/2
因此在二分時,通過?g 值求出的最小割來判斷正負,于是根據(jù)這個結果的正負就可以修改左右邊界,直到達到一個最優(yōu)值
void makeMap(double g) {memset(head,-1,sizeof(head)); tot=0;for (int i = 1; i <= n; i++) {//原圖中的點addedge(S, i, m * 1.0);addedge(i, T, m + 2 * g - degree[i]);}for (int i = 0; i < m; i++) {//原圖中的邊addedge(pastEdge[i].first, pastEdge[i].second, 1.0);addedge(pastEdge[i].second, pastEdge[i].first, 1.0);} }【例題】
- Dropping tests(POJ-2976)(標準模型+二分解法):點擊這里
- Dropping tests(POJ-2976)(標準模型+Dinkelbach 算法):點擊這里
- Desert King(POJ-2728)(最優(yōu)比率生成樹+二分解法):點擊這里
- Desert King(POJ-2728)(最優(yōu)比率生成樹+Dinkelbach 算法):點擊這里
- Sightseeing Cows(POJ-3621)(最優(yōu)比率環(huán)+二分解法):點擊這里
- Hard Life(POJ-3155)(最大密度子圖+最大權閉合圖):點擊這里
- Hard Life(POJ-3155)(最大密度子圖+最小割):點擊這里
總結
以上是生活随笔為你收集整理的分治 —— 01 分数规划的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最长高地(51Nod-2509)
- 下一篇: 连续整数的和(51Nod-1138)