1. 程式人生 > >【洛谷3232】[HNOI2013] 遊走(貪心+高斯消元)

【洛谷3232】[HNOI2013] 遊走(貪心+高斯消元)

點此看題面

大致題意:一個無向連通圖,小Z1號頂點出發,每次隨機選擇某條邊走到下一個頂點,並將ans加上這條邊的編號,走到N號頂點時結束。請你對邊進行編號,使總分期望值最小。

一個貪心的思想

由於貪心的思想,我們肯定是給期望訪問次數最大的邊編號為1,第二大的編號為2,第三大的編號為3,以此類推。

那麼我們應該怎麼求出邊的期望呢?

由於邊的期望可以由點的期望轉化得來,因此只要求出了點的期望,就能求出邊的期望。

那麼怎麼求出點的期望呢?

這時就需要用高斯消元了。

如何求出點的期望

下面是一張無向圖。

這裡寫圖片描述

如果我們用

Si來表示編號為i的節點被經過的期望次數,那麼顯然:

S1=S25+S33+S43+S52+S63+1

即編號為x的點的期望Sx=Sidegi,其中i為與x有邊相連的節點。

像這樣,我們可以將每一個點的期望都用其他點的期望來表示。

還是以S1為例,我們可以將這個式子移項:

S1S25S33S43S52S63=1

將每個式子都進行這樣的轉換之後,就可以通過高斯消元來求解出每一個Si

其中要注意的是,每一個式子中Sn的係數皆為0(因為遊走在走到

n號節點時結束),且第1個式子等號右邊的值為1(因為遊走從1號節點開始),而其他式子等號右邊的值皆為0

從點的期望到邊的期望

接下來的問題是,如何通過點的期望求出邊的期望。

Ei表示編號為i的邊被經過的期望次數,且編號為i的邊連線的兩個節點為xiyi,由於期望的性質,我們可以得到:

Ei=Sxidegxi+Syidegyi

這樣就可以輕鬆求出每條邊的期望了。

然後,按照開頭所述的貪心思想,就能輕鬆求解該題了。

程式碼

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y)) #define abs(x) ((x)<0?-(x):(x)) #define LL long long #define ull unsigned long long #define swap(x,y) (x^=y,y^=x,x^=y) #define Fsize 100000 #define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++) #define N 500 #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].from=x,e[ee].to=y,++deg[x]) char Fin[Fsize],*FinNow=Fin,*FinEnd=Fin; using namespace std; const double eps=1e-15; int n,m,ee=0,lnk[N+5],deg[N+5]; struct edge { int from,to,nxt; double val; }e[N*N+5]; inline bool cmp(edge x,edge y) { return x.val-y.val>eps; } struct Gauss//高斯消元 { double a[N+5][N+5],s[N+5]; inline void GetDataA(int x,int y,double v) {a[x][y]+=v;} inline void GetDataS(int x,double v) {s[x]=v;} inline void FindLine(int x) { register int i=x,j;register double t; while(fabs(a[i][x])<eps) ++i; for(t=s[i],s[i]=s[x],s[x]=t,j=1;j<=n;++j) t=a[i][j],a[i][j]=a[x][j],a[x][j]=t; } inline double GetAns() { register int i,j,k;register double delta,ans=0; for(i=1;i<n-1;++i) { FindLine(i); for(j=i+1;j<n;++j) for(s[j]+=(delta=-a[j][i]/a[i][i])*s[i],k=1;k<=n;++k) a[j][k]+=delta*a[i][k]; } for(i=n-1;i;--i) for(s[i]/=a[i][i],j=i-1;j;--j) s[j]-=a[j][i]*s[i]; for(i=2;i<=ee;++(++i)) e[i].val=s[e[i].from]/deg[e[i].from]+s[e[i].to]/deg[e[i].to]; for(sort(e+1,e+ee+1,cmp),i=1;i<=ee;++i) ans+=e[i].val*i; return ans; } }S; inline void read(int &x) { x=0;static char ch; while(!isdigit(ch=tc())); while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc())); } int main() { register int i,j,x,y; for(read(n),read(m),i=1;i<=m;++i) read(x),read(y),add(x,y),add(y,x); for(i=1;i<n;++i) for(S.GetDataA(i,i,1),j=lnk[i];j;j=e[j].nxt) if(e[j].to^n) S.GetDataA(i,e[j].to,-1.0/deg[e[j].to]);//根據上面推匯出的式子,初始化高斯消元的式子 return S.GetDataS(1,1),printf("%.3lf",S.GetAns()),0;//得出答案並輸出 }