[BZOJ2069][POI2004]ZAW

BZOJ2069

描述php

在Byte山的山腳下有一個洞穴入口. 這個洞穴由複雜的洞室通過隧道鏈接構成. 洞穴的入口是一條筆直通向「前面洞口」的道路. 隧道互相都不交叉(他們只在洞室相遇). 兩個洞室要麼就經過隧道鏈接起來,要麼就通過若干隧道間接的相連. 如今決定組織辦一個'King's of Byteotia Cup' 比賽. 參賽者的目標就是任意選擇一條路徑進入洞穴並儘快出來便可. 一條路徑必須通過除了「前面洞口」以外還至少要通過其餘一個洞室.一條路徑中一個洞不能重複通過(除了「前面洞室」之外),相似的一條隧道也不能重複通過. 一個著名的洞穴探險家 Byteala 正準備參加這個比賽. Byteala 已經訓練了數月並且他已得到了洞穴系統的一套詳細資料. 對於每條隧道他都詳細計算了從兩個方向通過所須要的時間. 通過一個洞室的時間很短能夠忽略不記. 如今Byteala 向計算一條符合條件的最優路徑.c++

輸入算法

第一行有兩個數n 和 m (3 <= n <= 5000, 3 <= m <= 10000) 分別表示洞室的數目以及鏈接他們的隧道的數目. 洞室從1 到 n編號. 「前面洞室」的編號爲1. 接下來m 行描述了全部的隧道. 每行四個整數a,b,c,d 表示從洞室a到洞室b須要c分鐘的時間,而從洞室b到洞室a須要d分鐘的時間, 1 <= a,b <= n, a <> b, 1 <= c,d <= 10000. 你能夠假設符合要求的路徑確定存在.數組

輸出flex

輸出一行,最少須要多少時間完成比賽.優化

輸入樣例 1spa

3 3
1 2 4 3
2 3 4 2
1 3 1 1

輸出樣例 1blog

6

來源get

[POI2004]input


 

給你們提供一個測評地點吧:https://www.luogu.org/problemnew/show/T79047,你們也能夠加入個人團隊。數據均隨機生成,生成數據的代碼爲:

#include <cstdio>
#include <ctime>
#include <algorithm>
using namespace std;
const int MAXN=10000;
int n,m,k;

int main()
{
	srand((unsigned)time(NULL));
	n=rand()%4997+13;m=rand()%9997+3;
	printf("%d %d\n",n,m);
	k=rand()%(m/2)+2;
	for (int i=1;i<=k;i++)
	{
		if (rand()%2==1) printf("1 %d %d %d\n",rand()%n+1,rand()%MAXN+1,rand()%MAXN+1);
		else printf("%d 1 %d %d\n",rand()%n+1,rand()%MAXN+1,rand()%MAXN+1);
	}
	int u,v;
	for (int i=k+1;i<=m;i++)
	{
		u=rand()%n+1;v=rand()%n+1;
		while (v==n)v=rand()%n+1;
		printf("%d %d %d %d\n",u,v,rand()%MAXN+1,rand()%MAXN+1);
	}
	return 0;
}

 

這個題目的意思是,從洞口$1$進入,通過其餘至少$1$個洞口後,再從洞口$1$出來,每一個洞口只能通過一次(洞口$1$除外)。求最短路徑。

這道題目因爲起點和終點同樣,咱們通常的$SPFA$、$dijkstra$等等都無論用了,更可惡的是,因爲每條邊方向不一樣,權值也不一樣,這使得$Floyd$也很差用了。那麼,咱們該怎麼辦呢?

咱們要把它轉化爲求普通的最短路!

咱們把與頂點$1$相連的點記錄下來,分別求最短路,而後再加上到點1的距離就好了。因而,你會驚奇的發現$Time~Limit~Exceeded$。那怎麼辦呢??

咱們能夠把與$1$相連的點分爲兩組,一下求這麼多的最短路。對與分組,二進制枚舉就能夠了。

AC代碼:

#include <queue>
#include <cstdio>
#include <bitset>
#include <cstring>
using namespace std;

int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0' || c>'9'){if (c=='-')f=-1;c=getchar();}
	while (c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
	return x*f;
}

const int MAXN=5005;
const int MAXM=20005;
int n,m,c,id,now,fir;

struct Qu
{
	int dot,dis;
	
	bool operator < (const Qu tmp) const 
	{
		return dis>tmp.dis;
	}
};

struct edge
{
	int v,w,nx;
}set[MAXM],key[MAXN];
int head[MAXN],dis[MAXN];
bitset<MAXN> vis;
priority_queue<Qu> Q;

inline void Addedge(int u,int v,int w)
{
	id++;set[id].v=v;set[id].w=w;set[id].nx=head[u];
	head[u]=id;
}

inline void insert_edge(int v,int w)
{
	now++;key[now].v=v;key[now].w=w;key[now].nx=fir;
	fir=now;
}

inline void init()
{
	int u,v,a,b;
	n=read();m=read();
	for (int i=1;i<=m;i++)
	{
		u=read();v=read();a=read();b=read();
		Addedge(u,v,a);Addedge(v,u,b);
		if (u==1)insert_edge(v,b);
		if (v==1)insert_edge(u,a);
	}
	int x=n;
	while (x>0) {c++;x/=2;}
}

inline void dijkstra()
{
	vis.reset();vis.set(1);
	for (int k=head[1];k>0;k=set[k].nx) Q.push((Qu){set[k].v,dis[set[k].v]});
	int u,v;
	while (!Q.empty())
	{
		u=Q.top().dot;Q.pop();
		vis.set(u);
		for (int k=head[u];k>0;k=set[k].nx)
		{
			v=set[k].v;
			if (dis[u]+set[k].w<dis[v])
			{
				dis[v]=dis[u]+set[k].w;
				if (!vis[v])Q.push((Qu){v,dis[v]});
			}
		}
	}
}

int main()
{
	init();
	int ans=0x3f3f3f3f;
	for (int i=(1<<c);i>0;i>>=1)
	{
		memset(dis,0x3f,sizeof(dis));
		for (int k=head[1];k>0;k=set[k].nx)
			if (set[k].v&i)dis[set[k].v]=set[k].w;
		dijkstra();
		for (int k=fir;k>0;k=key[k].nx)
			if (~key[k].v&i)ans=min(ans,dis[key[k].v]+key[k].w);
		memset(dis,0x3f,sizeof(dis));
		for (int k=head[1];k>0;k=set[k].nx)
			if (~set[k].v&i)dis[set[k].v]=set[k].w;
		dijkstra();
		for (int k=fir;k>0;k=key[k].nx)
			if (key[k].v&i)ans=min(ans,dis[key[k].v]+key[k].w);
	}
	printf("%d\n",ans);
	return 0;
}

 而後,咱們須要證實這個方案的正確性。

首先,咱們很容易想到初版無腦枚舉,就是枚舉起點和終點,這樣的話,枚舉的複雜度是$(n*n)$,再加上$dijkstra$的時間複雜度$O(n*n)$,總的時間複雜度就是$O(n^4)$。但這種方法能夠$TLE$飛。因而,便要進行一點優化:先枚舉起點,求一遍最短路並經過$dis_i$數組記錄從起點到點$i$的距離,時間複雜度即是$O(n^3)$,效率是有一點提升,但本題的數據實在是太坑了,這個算法也被卡掉了。

因此還要找更加優化的算法。咱們能夠把數字的二進制列出來:

十進制數 二進制數
$1$ $1$
$2$ $10$
$3$ $11$
$4$ $100$
$5$ $101$
$6$ $110$
$7$ $111$
$8$ $1000$
$.~.~.$ $.~.~.$

 

能夠發現,每一個數至少有一個位上的數的差異。咱們能夠 枚舉$1$的位置,這樣一來,咱們能夠把此位爲$1$的點分到$A$組,爲零的點分到$B$組,從$A$組出發求最短路,距離就是點$1$到他們的距離。因而枚舉的複雜度便變爲了$(log^2~n)$,因而總的時間複雜度爲$O(log^2~n*n^2)$。
 
$Please~give~a~like.Thanks.$
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息