01分數規劃的兩道例題

~hsm~發表於2019-02-22

POJ 2728 Desert King 最優比率生成樹

title

POJ 2728

wrong reason

使用printf不論是進行double輸出還是float輸出都要用%.3f。
查了半天,終於找到了原因:POJ就是個牛laji玩意兒。

問:有人告訴我不能在printf中使用%lf。為什麼printf()用%f輸出double型,而scanf卻用%lf呢?

答:printf的%f說明符的確既可以輸出float型又可以輸出double型。根據“預設引數提升”規則(在printf這樣的函式的可變引數列表中,不論作用域內有沒有原型,都適用這一規則)float型會被提升為double型。因此printf()只會看到雙精度數。參見問題15.2。

(嚴格地講,%lf在printf下是未定義的,但是很多系統可能會接受它。要確保可移植性,就要堅持使用%f。)
轉自qu317058542_scu

analysis

好了,言歸正傳,開始分析一下這道題。
首先,解釋一下,歐幾里得距離是個什麼東西:在數學中,歐幾里得距離或歐幾里得度量是歐幾里得空間中兩點間“普通”(即直線)距離。——摘自某度

其實原題就是求 MIN(ΣCiXiΣDiXi)Xi[01]MIN(\dfrac {\Sigma _{CiXi}}{\Sigma _{DiXi}}),Xi∈[0,1] ,對每個生成樹,設其比率r=ΣCiXiΣDiXir=\dfrac {\Sigma _{CiXi}}{\Sigma _{DiXi}},可得ΣCiXiΣDiXir=01\Sigma _{CiXi} - \Sigma _{DiXi}*r=0(條件1)

那麼對於所有的生成樹,顯然ΣCiXiΣDiXimin(r)>=0\Sigma _{CiXi} - \Sigma _{DiXi}* min(r) >= 0,當 ΣCiXiΣDiXi=min(r)\dfrac {\Sigma _{CiXi}}{\Sigma _{DiXi}} = min(r)時,等號成立。
而我們現在不知道min(r)min(r)是多少,只好進行列舉,對每個列舉的r ,構建新的權值(CiDirCi-Di*r),然後求最小生成樹, 為什麼求最小呢?
我的理解就是這是為了尋找使得生成樹的總權值為0的可能性,因為只有當其等於0 的時候,才滿足了條件1 這個條件, 說明這個rr是可行的,並且如果rr列舉到值為min(r)min(r)時,其最小生成樹的的總權值必然恰好等於0,但是如果不能等於0, 比如大於0, 顯然是對該r值,所有的生成樹上無論如何也滿足不了條件1,說明rr值就是偏小了。同理如果小於0,rr值是偏大的,說明可能存在某些生成樹使得滿足條件1,而我們的目標是在滿足條件1的情況下使得rr最小,摘自sdj

code

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=1006,inf=0x3f3f3f3f;
const double eps=1e-6;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int n;
struct mst
{
	int x,y,z;
}e[maxn];
double a[maxn][maxn],b[maxn][maxn],c[maxn][maxn],d[maxn];
bool v[maxn];
inline double s(int i,int j)
{
	return sqrt((e[i].x-e[j].x) * (e[i].x-e[j].x) + (e[i].y-e[j].y) * (e[i].y-e[j].y));
}
inline double check(double k)
{
	for (int i=1;i<=n;++i)
		for (int j=i;j<=n;++j)
			if (i==j) c[i][j]=inf;
			else c[i][j]=c[j][i]=a[i][j]-k*b[i][j];
	memset(v,0,sizeof(v));
	for (int i=1;i<=n;++i)
		d[i]=inf;
	d[1]=0;
	double ans=0;
	while (1)
	{
		int x=0;
		for (int i=1;i<=n;++i)
			if (!v[i] && (!x || d[x]>d[i]))
				x=i;
		if (!x) break;
		v[x]=1;
		ans+=d[x];
		for (int i=1;i<=n;++i)
			d[i]=min(d[i],c[x][i]);
	}
	return ans;
}
inline void Desert_King()
{
	for (int i=1;i<=n;++i)
		read(e[i].x),read(e[i].y),read(e[i].z);
	double num=0;
	for (int i=1;i<=n;++i)
		for (int j=1;j<=n;++j)
		{
			num+=(a[i][j]=a[j][i]=abs(e[i].z-e[j].z));
			b[i][j]=b[j][i]=s(i,j);
		}
	double l=0,r=num;
	while (l+eps<=r)
	{
		double mid=(l+r)/2;
		if (check(mid)>=0) l=mid;
		else r=mid;
	}
	printf("%.3f\n",l);
}
int main()
{
	while (cin>>n && n) Desert_King();
	return 0;
}

「BZOJ1690」[Usaco2007 Dec] 奶牛的旅行 最優比率環

titlep1938

POJ 3621

描述 Description
  作為對奶牛們辛勤工作的回報,Farmer John決定帶她們去附近的大城市玩一天。旅行的前夜,奶牛們在興奮地討論如何最好地享受這難得的閒暇。
  很幸運地,奶牛們找到了一張詳細的城市地圖,上面標註了城市中所有L(2 <= L <= 1000)座標誌性建築物(建築物按1…L順次編號),以及連線這些建築物的P(2 <= P <= 5000)條道路。按照計劃,那天早上Farmer John會開車將奶牛們送到某個她們指定的建築物旁邊,等奶牛們完成她們的整個旅行並回到出發點後,將她們接回農場。由於大城市中總是寸土寸金,有的道路都很窄,政府不得不把它們都設定為通行方向固定的單行道。
  儘管參觀那些標誌性建築物的確很有意思,但如果你認為奶牛們同樣享受穿行於大城市的車流中的話,你就大錯特錯了。與參觀景點相反,奶牛們把走路定義為無趣且令她們厭煩的活動。對於編號為i的標誌性建築物,奶牛們清楚地知道參觀它能給自己帶來的樂趣值F_i (1 <= F_i <= 1000)。相對於奶牛們在走路上花的時間,她們參觀建築物的耗時可以忽略不計。
  奶牛們同樣仔細地研究過城市中的道路。她們知道第i條道路兩端的建築物L1_i和L2_i(道路方向為L1_i -> L2_i),以及她們從道路的一頭走到另一頭所需要的時間T_i(1 <= T_i <= 1000)。
  為了最好地享受她們的休息日,奶牛們希望她們在一整天中平均每單位時間內獲得的樂趣值最大。當然咯,奶牛們不會願意把同一個建築物參觀兩遍,也就是說,雖然她們可以兩次經過同一個建築物,但她們的樂趣值只會增加一次。順便說一句,為了讓奶牛們得到一些鍛鍊Farmer John要求奶牛們參觀至少2個建築物。
  請你寫個程式,幫奶牛們計算一下她們能得到的最大平均樂趣值。
輸入格式 Input Format
第1行: 2個用空格隔開的整數:L 和 P
第2…L+1行: 第i+1行僅有1個整數:F_i
第L+2…L+P+1行: 第L+i+1行用3個用空格隔開的整數:L1_i,L2_i以及T_i, 描述了第i條道路。
輸出格式 Output Format
輸出1個實數,保留到小數點後2位(直接輸出,不要做任何特殊的取整操作),表示如果奶牛按題目中描述的一系列規則來安排她們的旅行的話,她們能獲得的最大平均樂趣值
樣例輸入 Sample Input
5 7
30
10
10
5
10
1 2 3
2 3 2
3 4 5
3 5 2
4 5 5
5 1 3
5 2 2
樣例輸出 Sample Output
6.00
輸出說明:
如果奶牛選擇1 -> 2 -> 3 -> 5 -> 1的旅行路線,她們能得到的總樂趣值為60,為此她們得花費10單位的時間在走路上。於是她們在這次旅行中的平均樂趣值為6。如果她們走2 -> 3 -> 5 -> 2的路線,就只能得到30/6 = 5的平均樂趣值。並且,任何去參觀建築物4的旅行路線的平均樂趣值都沒有超過4。
時間限制 Time Limitation
1s
註釋 Hint
來源 Source
usaco 2007 dec gold sightsee

wrong reason

spfa函式的傳參型別錯誤的設成了int,害我一直WA。

analysis

首先要證明的是奶牛最後選到一定是一個簡單環,如下,
假設最優解不是簡單環,則其中必定有一個重複點,設為c1,對於這個點隔開到也是兩個環,我們設兩個環中除了這個點其他點權值和分別為,c2,c3;邊權值為 a1,s2;
由於它是最優解所以有 :
1.(c1+c2+c3)/(a1+a2)&gt;(c1+c2)/a11.(c1+c2+c3)/(a1+a2) &gt; (c1+c2)/a1
2.(c1+c2+c3)/(a1+a2)&gt;(c2+c3)/a22.(c1+c2+c3)/(a1+a2) &gt; (c2+c3)/a2
1a1c3&gt;a2(c1+c2)由1有:a1*c3 &gt; a2*(c1+c2)
2a2c1&gt;a1(c2+c3)由2有:a2*c1 &gt; a1*(c2+c3)
a1c3&gt;a2c2+a1(c2+c3)所以:a1*c3 &gt; a2*c2+a1*(c2+c3)
顯然錯誤,至此可以有結論,最優解必定是簡單環

基於這個結論在思考,發現如果判斷某個解是否行,我們考慮的是簡單環!!因為如果存在這樣一個簡單環則顯然成立,不存在則必定不可行;所以我們可以把邊權值變成, mid*t-F[to],如果存在負權環則mid是可行解。

綜上發現2分答案+判負環是正確解法,摘自lzqxh

code

/*******************************************************
	Problem: 3621		User: sjh2021
	Memory: 708K		Time: 532MS
	Language: G++		Result: Accepted
********************************************************/

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=1001,maxm=5005;
const double eps=1e-3;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-')  ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int n,m,happy[maxn];
int ver[maxm],edge[maxm],Next[maxm],head[maxn],len;
inline void add(int x,int y,int z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
double dist[maxn];
int v[maxn],cnt[maxn];
inline bool spfa(double k)
{
	memset(cnt,0,sizeof(cnt));
	memset(v,0,sizeof(v));
	for (int i=1;i<=n;++i)
		dist[i]=1e15;
	queue<int>q;
	dist[1]=0,v[1]=1;
	q.push(1),++cnt[1];
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		v[x]=0;
		for (int i=head[x];i;i=Next[i])
		{
			int y=ver[i],z=edge[i];
			if (dist[y]>dist[x]+k*z-happy[x])
			{
				dist[y]=dist[x]+k*z-happy[x];
				if (!v[y])
				{
					q.push(y),v[y]=1;
					if (++cnt[y]>n) return true;
				}
			}
		}
	}
	return false;
}
int main()
{
	read(n);read(m);
	for (int i=1;i<=n;++i)
		read(happy[i]);
	for (int i=1;i<=m;++i)
	{
		int x,y,z;
		read(x);read(y);read(z);
		add(x,y,z);
	}
	double l=0,r=10000;
	while (l+eps<r)
	{
		double mid=(l+r)/2;
		if (spfa(mid)) l=mid;
		else r=mid;
	}
	printf("%.2f\n",l);
	return 0;
}

放一個以前寫的1000MS過得程式碼。碼風有點不一樣

/******************************************
	Problem: 3621		User: sjh2021
	Memory: 1308K		Time: 1000MS
	Language: G++		Result: Accepted
*******************************************/

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
#define _ 100010
using namespace std;
const double eps=1e-8;
struct rec
{
	int x,y,z;
}e[_];
inline int read()
{
	int f=1,num=0;
	char ch=getchar();
	while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
	while (ch>='0'&&ch<='9') num=(num<<1)+(num<<3)+ch-'0',ch=getchar();
	return num*f;
}
int n,m,happy[_];
int ver[_],Next[_],head[_],len;
double edge[_],l,r;
void add(int x,int y,double z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
double dist[_];
bool v[_];
int cnt[_];
queue<int>q;
inline bool spfa()
{
	for (int i=1;i<=n;++i)	dist[i]=-1e9;
	memset(cnt,0,sizeof(cnt));
	memset(v,0,sizeof(v));
	dist[1]=0;
	q.push(1);
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		v[x]=0;
		for (int i=head[x];i;i=Next[i])
		{
			int y=ver[i];
			double z=edge[i];
			if (dist[y]+eps<dist[x]+z)
			{
				dist[y]=dist[x]+z;
				if (!v[y])
				{
					q.push(y),v[y]=1;
					if (++cnt[y]>n) return true;
				}
			}
		}
	}
	return false;
}
inline bool check(double v)
{
	memset(head+1,0,sizeof(int)*n);
	len=0;
	for (int i=1;i<=m;++i)
		add(e[i].x,e[i].y,happy[e[i].x]-e[i].z*v);
	if (spfa()) return true;
	else return false;
}
int main()
{
	n=read(),m=read();
	for (int i=1;i<=n;++i)
		happy[i]=read();
	for (int i=1;i<=m;++i)
	{
		int x=read(),y=read(),z=read();
		e[i]=(rec){x,y,z};
		r+=z;
	}
	while (l+eps<r)
	{
		double mid=(l+r)/2.0;
		if (check(mid)) l=mid;
		else r=mid;
	}
	printf("%.2f",l);
	return 0;
}

相關文章