[HNOI2009]最小圈
阿新 • • 發佈:2019-02-12
.com acs || 分答 code www. amp memset class 我們直接二分要求的答案x,check時把每條邊的邊權看作\(c_i-x\)
傳送門
題意:在帶權有向圖中,求圖中所有環的平均值的最小值(環的平均值即構成環的所有邊的邊權的平均值).
分析:理解題意,我們要求的實際上就是\(\frac{\sum_{i=1}^k{c_i}}{k}\)
令\(x=\frac{\sum_{i=1}^k{c_i}}{k}\)
整理得\(\sum_{i=1}^k{c_i}-k*x=0\)
繼續得\(\sum_{i=1}^k(c_i-x)=0\)
令\(f(x)=\sum_{i=1}^k(c_i-x)\)
f(x)隨x增大而減小,即f(x)是一個遞減函數,既然具有單調性,就要想到二分答案.
我們直接二分要求的答案x,check時把每條邊的邊權看作\(c_i-x\) ,如果此次二分的值合法,即\(\frac{\sum_{i=1}^k{c_i}}{k}<x\),根據上面的推導得\(\sum_{i=1}^k(c_i-x)<0\),即圖中存在一個負環.
所以本題就轉換為了二分x,把每條邊權看作\(c_i-x\),然後判斷圖中是否存在負環.
我寫的是dfs+spfa判負環(如果不會判負環,請看廣告).
註意本題是實數域上的二分答案.
int n,m,visit[3005]; int tot,head[3005],nxt[10005],to[10005]; double eps=1e-10; //要求保留8位小數,二分精度可以設置為1e-(8+2) double w[10005],dis[3005]; void add(int a,int b,double c){ nxt[++tot]=head[a]; head[a]=tot; to[tot]=b; w[tot]=c; } bool dfs_spfa(int x,double mid){ visit[x]=1; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; //記得邊權看作w[i]-mid if(dis[y]>dis[x]+w[i]-mid){ dis[y]=dis[x]+w[i]-mid; if(visit[y]||dfs_spfa(y,mid)) return 1; } } visit[x]=0; return 0; } bool check(double mid){ memset(visit,0,sizeof(visit)); for(int i=1;i<=n;i++)dis[i]=0; //這裏為什麽dis數組全部初始化為零我也不懂 //反正我試了賦值為無窮大是錯的 //(我不會說出來我無腦試了好幾個初值) for(int i=1;i<=n;i++) if(dfs_spfa(i,mid))return 1; return 0; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int a,b;double c; scanf("%d%d%lf",&a,&b,&c); add(a,b,c); } double l=-1e9,r=1e9,mid; //註意一下二分邊界,因為環的最小平均值可能為負數 while(l+eps<r){//實數二分答案模板 mid=(l+r)/2.0; if(check(mid))r=mid; else l=mid; } printf("%.8lf\n",r); return 0; }
[HNOI2009]最小圈