P3199 【[HNOI2009]最小圈】
阿新 • • 發佈:2020-07-26
疑似三倍經驗
因為和機房一些大佬一起做的這道題,所以emmm他們貌似也寫了題解,在做這道題的時候也參照了其他大佬寫的一些題解,所以如果程式有雷同請見諒(手動鞠躬)
題目也是莫名其妙地給了一大串數學式,簡潔地重新說一下題目
給你一張圖,圖中有環,定義一個環的平均值為環的邊權和÷環中點的個數,那麼就應該有了一中非常暴力的思路
- 想辦法找出來圖中所有的環並求出其平均值,再比較出最小值
這個方法確實怎麼想都很暴力,但是一點也不好實現,可能是我太弱,我實在想不太出來有什麼演算法可以找出來所有的環,所以這種思路先給我PASS掉
而正解呢?應該是二分答案。為什麼?
- 既然我們找不出來環,我們逆向思維,直接列舉平均值,看是否會有一個環符合條件,那麼這道題就變成了:二分列舉平均值,找到是否有環符合條件
若我們此時列舉的平均值為\(ans\),有\(k\)個字串,那麼就有
\(ans * k = len1 + len2 + len3 + ... + lenk\)
那道這個式子之後,我們對它進行移項
\(0=len1-ans+len2-ans+len3-ans+...+lenk-ans\)
那麼對於滿足以下式子,就可以判斷是環了,所以在跑\(SPFA\)更新距離的時候,就應該像下面這樣
\(0 \leq len1-ans+len2-ans+len3-ans+...+lenk-ans\)
所以這裡就直接給程式了(三道題的)
P3199 [HNOI2009]最小圈
#include<bits/stdc++.h> using namespace std; const int MAXN=30000000+51; const double INF=(1e5)*1.0; const double eqs=1e-9; int n,m; struct node { int net,to; double z; } e[MAXN]; int head[MAXN],tot; void add(int x,int y,double z) { e[++tot].net=head[x]; e[tot].to=y; e[tot].z=z; head[x]=tot; } double d[MAXN]; bool v[MAXN],flag; bool spfa(int x,double k) { v[x]=true; for(register int i=head[x]; i; i=e[i].net) { int y=e[i].to; double z=e[i].z; if(d[y]>d[x]+z-k) { d[y]=d[x]+z-k; if(v[y]==true||spfa(y,k)==true) return true; } } v[x]=false; return false; } bool check(double x) { for(register int i=1; i<=n; i++) { d[i]=20040915; v[i]=false; } for(register int i=1; i<=n; i++) { if(spfa(i,x)==true) return true; } return false; } int main() { scanf("%d%d",&n,&m); for(register int i=1; i<=m; i++) { int x,y; double z; scanf("%d%d%lf",&x,&y,&z); add(x,y,z); } double l=-INF,r=INF; while(r-l>eqs) { double mid=(l+r)/2; if(check(mid)==true) r=mid; else l=mid; } printf("%.8lf",l); return 0; }
#include <bits/stdc++.h>
using namespace std;
int T,n,m,u,v,w,tot;
double dis[520010];
int vis[520010],head[520010];
struct node {
int to,net;
double val;
} e[520010];
inline void add(int u,int v,double w) {
e[++tot].to=v;
e[tot].val=w;
e[tot].net=head[u];
head[u]=tot;
}
inline bool dfs(int now,double x) {
vis[now]=1;
for(register int i=head[now];i;i=e[i].net) {
int v=e[i].to;
if(dis[v]>dis[now]+e[i].val-x) {
dis[v]=dis[now]+e[i].val-x;
if(vis[v]==1||dfs(v,x)==true) return true;
}
}
vis[now]=0;
return false;
}
inline bool check(double x) {
for(register int i=1;i<=n;i++) {
vis[i]=0;
dis[i]=20050206;
}
for(register int i=1;i<=n;i++) {
if(dfs(i,x)==true) return true;
}
return false;
}
int main() {
scanf("%d",&T);
for(register int k=1;k<=T;k++) {
tot=0;
for(register int i=1;i<=n;i++) head[i]=0;
scanf("%d%d",&n,&m);
for(register int i=1;i<=m;i++) {
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
double l=-10000000,r=10000000;
while(r-l>1e-10) {
double mid=(l+r)/2;
if(check(mid)==true) r=mid;
else l=mid;
}
printf("Case #%d: ",k);
if(l==10000000) puts("No cycle found.");
else printf("%.2lf\n",l);
}
return 0;
}