poj 3111 K Best (牛頓迭代法)
阿新 • • 發佈:2018-12-20
牛頓迭代法參考連結:我愛維基
首先,選擇一個接近函式零點的,計算相應的和切線斜率(這裡表示函式的導數)。然後我們計算穿過點並且斜率為的直線和軸的交點的座標,也就是求如下方程的解:
我們將新求得的點的座標命名為,通常會比更接近方程的解。因此我們現在可以利用開始下一輪迭代。迭代公式可化簡為如下所示:
我們最後選擇一個精度範圍就行了。
題目連結:poj 3111
題意:給你n個價值為v,質量為w,讓你選擇k個,滿足單位價值最大。
題解:這題是個最大值最大化例題,但這題可以用迭代法來解決。
先取前k個元素算出S0 =∑(vi/wi) 作為初始值,然後對每一個元素(n個)求yi=vi-s0*wi,對yi從大到小排序,取前k個元素算出S,重複上面的運算(每次迴圈後把S的值賦給S0,然後新一輪迴圈時S有通過S0計算出來),直到fabs(S-S0)<=eps,滿足精度要求。
正確性證明:
證明其正確性,只要證明每次迭代的S都比上一次的大即可,也即迭代過程中S是單調遞增的,因為給定的是有限集,故可以肯定,S必存在最大值,即該迭代過程是收斂的。下面證明單調性:
假設上輪得到的S1,則在n個元素中必存在k個元素使S1=∑(vi/wi),變形可得到∑vi-S1*∑wi=0,現對每個元素求yi=vi-S1*wi,可知必存在k個元素使∑yi=∑vi-s1*∑wi=0, 所以當我們按y排序並取前k個元素作為求其∑y時,其∑y>=0,然後對和式變形即可得到S1=((∑v-∑y)/∑w)<=(∑v/∑w)=s2,即此迭代過程是∑y是收斂的,當等號成立時,此S即為最大值。
ei,這個好像跟上面的牛頓迭代法聯絡不是很大,自己體會了。
程式碼:
#include<algorithm> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int maxn=100010; const double esp=1e-8; struct node{ int v,w,id; double val; bool operator < (const node &a) const { return val>a.val; } }num[maxn]; int n,k; double get() { double sumv=0,sumw=0; for(int i=1;i<=k;i++) sumv+=num[i].v,sumw+=num[i].w; return sumv/sumw; } int main() { while(~scanf("%d%d",&n,&k)) { for(int i=1;i<=n;i++) { scanf("%d%d",&num[i].v,&num[i].w); num[i].id=i; } double s2=get(),s1; do{ s1=s2; for(int i=1;i<=n;i++){ num[i].val=num[i].v-s1*num[i].w; } sort(num+1,num+1+n); s2=get(); }while(fabs(s2-s1)>=esp); for(int i=1;i<k;i++) printf("%d ",num[i].id); printf("%d\n",num[k].id); } return 0; }
-