2020.05.30日常總結

洛谷P1768 天路\color{green}{\texttt{洛谷P1768 天路}}

[Problem]\color{blue}{\texttt{[Problem]}}

  • 給你一個含有 nn 個點 mm 條有向邊的圖,每條邊有兩個值:vvpp,分別表示這條路的價值和花費。
  • 一條路的收益定義爲所有邊的 vv 值的和比上 pp 值的和。
  • 求一條沒有相同的邊的環,使得其收益最大。

[Solution]\color{blue}{\texttt{[Solution]}}

二分答案。記當前二分出來的值爲 mid\texttt{mid},表示總收益 mid\geq \texttt{mid}

如果滿足,則:

vpmidvmid×pmid×pvmid×pv0\begin{aligned} \dfrac{\sum v}{\sum p} &\geq \texttt{mid}\\ \sum v &\geq \texttt{mid}\times \sum p\\ \texttt{mid}\times \sum p &\leq \sum v\\ \texttt{mid} \times \sum p - \sum v &\leq 0 \end{aligned}

於是,我們修改每條邊的邊權爲 mid×pv\texttt{mid} \times p - v 即可,求負環即可。

注意,bfs spfa 求負環的時間複雜度爲 O(nm)O(nm),可能會超時,所以我們賭一把,用時間複雜度不穩定的 dfs spfa

[code]\color{blue}{\texttt{[code]}}

const int M=20100,N=7010;
struct edge{//鏈式前向星 
	int next,to,v,p;
}e[(M<<1)+N];int h[N],tot,n,m;
void add(int a,int b,int c,int d){
	e[++tot]=(edge){h[a],b,c,d};h[a]=tot;
}
bool vis[N];double dis[N],l,r,mid;
inline bool dfs(int u,double mid){
	vis[u]=true;register int i;
	for(i=h[u];i;i=e[i].next){
		register int to=e[i].to;
		double w=e[i].p*mid-e[i].v;
		if (dis[to]>dis[u]+w){
			dis[to]=dis[u]+w;
			if (vis[to]) return true;
			if (dfs(to,mid)) return true;
		}
	}
	vis[u]=false;
	return false;
}
inline bool check(double mid){
	memset(dis,127,sizeof(dis));
	memset(vis,false,sizeof(vis));
	dis[0]=0;//注意特別初始化dis[0] 
	return dfs(0,mid);//返回spfa結果 
}
const double eps=1e-5;
int main(){
	scanf("%d%d",&n,&m);//點數;邊數 
	for(int i=1,a,b,v,p;i<=m;i++){
		scanf("%d%d%d%d",&a,&b,&v,&p);
		add(a,b,v,p);//往圖中加邊 
	}
	for(int i=1;i<=n;i++)
		add(0,i,0,0);//超級源 
	l=0;r=200;//初始二分上下界 
	while (l+eps<r){//注意精度 
		mid=(l+r)/2;//求出中點 
		if (check(mid)) l=mid;
		else r=mid;//修改邊界 
	}
	if (l==0) printf("-1");
	else printf("%.1lf",l);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章