【洛谷3232】[HNOI2013] 遊走(貪心+高斯消元)
阿新 • • 發佈:2018-12-10
大致題意:一個無向連通圖,小從號頂點出發,每次隨機選擇某條邊走到下一個頂點,並將加上這條邊的編號,走到號頂點時結束。請你對邊進行編號,使總分期望值最小。
一個貪心的思想
由於貪心的思想,我們肯定是給期望訪問次數最大的邊編號為,第二大的編號為,第三大的編號為,以此類推。
那麼我們應該怎麼求出邊的期望呢?
由於邊的期望可以由點的期望轉化得來,因此只要求出了點的期望,就能求出邊的期望。
那麼怎麼求出點的期望呢?
這時就需要用高斯消元了。
Link
如何求出點的期望
下面是一張無向圖。
如果我們用 來表示編號為的節點被經過的期望次數,那麼顯然:
即編號為的點的期望,其中為與有邊相連的節點。
像這樣,我們可以將每一個點的期望都用其他點的期望來表示。
還是以為例,我們可以將這個式子移項:
將每個式子都進行這樣的轉換之後,就可以通過高斯消元來求解出每一個。
其中要注意的是,每一個式子中的係數皆為(因為遊走在走到 號節點時結束),且第個式子等號右邊的值為(因為遊走從號節點開始),而其他式子等號右邊的值皆為。
從點的期望到邊的期望
接下來的問題是,如何通過點的期望求出邊的期望。
設表示編號為的邊被經過的期望次數,且編號為的邊連線的兩個節點為和,由於期望的性質,我們可以得到:
這樣就可以輕鬆求出每條邊的期望了。
然後,按照開頭所述的貪心思想,就能輕鬆求解該題了。
程式碼
#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;//得出答案並輸出
}