LOJ2360「NOIP2016」換教室

原題鏈接:https://loj.ac/problem/2360

換教室

題目描述

對於剛上大學的牛牛來說,他面臨的第一個問題是如何根據實際情況申請合適的課程。

在可以選擇的課程中,有2n2n節課程安排在nn個時間段上。在第ii個時間段上(1in)(1 \leq i \leq n),兩節內容相同的課程同時在不同的地點進行,其中,牛牛預先被安排在教室cic_i上課,而另一節課程在教室did_i進行。
在不提交任何申請的情況下,學生們需要按時間段的順序依次完成所有的nn節安排好的課程。如果學生想更換第i節課程的教室,則需要提出申請。若申請通過,學生就可以在第ii個時間段去教室did_i上課,否則仍然在教室cic_i上課。
由於更換教室的需求太多,申請不一定能獲得通過。通過計算,牛牛發現申請更換第ii節課程的教室時,申請被通過的概率是一個已知的實數kik_i,並且對於不同課程的申請,被通過的概率是互相獨立的。
學校規定,所有的申請只能在學期開始前一次性提交,並且每個人只能選擇至多mm節課程進行申請。這意味着牛牛必須一次性決定是否申請更換每節課的教室,而不能根據某些課程的申請結果來決定其他課程是否申請;牛牛可以申請白己最希望更換教室的mm門課程,也可以不用完這mm個申請的機會,甚至可以一門課程都不申請。

因爲不同的課程可能會被安排在不同的教室進行,所以牛牛需要利用課問時間從一間教室趕到另一間教室。
牛牛所在的大學有vv個教室,有ee條道路。每條道路連接兩間教室,並且是可以雙向通行的。由於道路的長度和擁堵程度不同,通過不同的道路耗費的體力可能會有所不同。當第ii1in11 \leq i \leq n - 1)節課結束後,牛牛就會從這節課的教室出發,選擇一條耗費體力最少的路徑前往下一節課的教室。

現在牛牛想知道,申請哪幾門課程可以使他因在教室問移動耗費的體力值的總和的期望值最小,請你幫他求出這個最小值。

輸入格式

第一行四個整數n,m,v,en, m, v, enn表示這個學期內的時間段的數量;mm表示牛牛最多可以申請更換多少節課程的教室;vv表示牛牛學校裏教室的數量;ee表示牛牛的學校裏道路的數量。
第二行nn個正整數,第ii1in1 \leq i \leq n)個正整數表示cic_i,即第ii個時間段牛牛被安排上課的教室;保證1civ1 \leq c_i \leq v
第三行nn個正整數,第ii1in1 \leq i \leq n)個正整數表示 did_i ,即第ii個時間段另一間上同樣課程的教室;保證1div1 \leq d_i \leq v
第四行nn個實數,第ii1in1 \leq i \leq n)個實數表示kik_i,即牛牛申請在第ii個時間段更換教室獲得通過的概率。保證0ki10 \leq k_i \leq 1
接下來ee行,每行三個正整數aj,bj,wja_j, b_j, w_j,表示有一條雙向道路連接教室 aj,bja_j, b_j,通過這條道路需要耗費的體力值是WjW_j;保證1aj,bjv,1wj1001 \leq a_j, b_j \leq v, 1 \leq w_j \leq 100
保證1n20001 \leq n \leq 20000m20000 \leq m \leq 20001v3001 \leq v \leq 3000e900000 \leq e \leq 90000
保證通過學校裏的道路,從任何一間教室出發,都能到達其他所有的教室。
保證輸入的實數最多包含33位小數。

輸出格式

輸出一行,包含一個實數,四舎五入精確到小數點後恰好22位,表示答案。你的輸出必須和標準輸出完全一樣纔算正確。
測試數據保證四舎五入後的答案和準確答案的差的絕對值不大於4×1034 \times 10 ^ {-3} 。(如果你不知道什麼是浮點誤差,這段話可以理解爲:對於大多數的算法,你可以正常地使用浮點數類型而不用對它進行特殊的處理)

樣例
輸入樣例

3 2 3 3
2 1 2
1 2 1
0.8 0.2 0.5
1 2 5
1 3 3
2 3 1

輸出樣例

2.80

數據範圍與提示

1n,m2000,1v300,1e900001 \leq n, m \leq 2000, 1 \leq v \leq 300, 1 \leq e \leq 90000

代碼

dp[i][j][1/0]dp[i][j][1/0]表示考慮前ii節課,申請換了jj節課,第i1i-1節課是/否申請了調換。

然後轉移就非常自然了,只是有點小長。。。

對於申請了調換的教室,我們就要考慮調換成功/失敗兩種情況,加上當前情況的距離乘以出現概率,取minmin即可。

代碼
#include<bits/stdc++.h>
using namespace std;
const int M=2005,N=305;
int c[M],d[M],dis[N][N],n,m,v,e;
double k[M],dp[M][M][2],ans=1e200;
void in()
{
	memset(dis,63,sizeof(dis));
	scanf("%d%d%d%d",&n,&m,&v,&e);
	for(int i=1;i<=n;++i)scanf("%d",&c[i]);
	for(int i=1;i<=n;++i)scanf("%d",&d[i]);
	for(int i=1;i<=n;++i)scanf("%lf",&k[i]);
	for(int i=1,a,b,w;i<=e;++i)
	scanf("%d%d%d",&a,&b,&w),dis[a][b]=min(dis[a][b],w),dis[b][a]=min(dis[b][a],w);
}
void ac()
{
	for(int p=1;p<=v;++p)for(int i=1;i<=v;++i)for(int j=1;j<=v;++j)
	if(dis[i][p]+dis[p][j]<dis[i][j])dis[i][j]=dis[i][p]+dis[p][j];
	for(int i=1;i<=v;++i)dis[i][0]=dis[0][i]=dis[i][i]=0;
	memset(dp,127,sizeof(dp));dp[1][0][0]=dp[1][1][1]=0;
	for(int i=2,j;i<=n;++i)for(dp[i][0][0]=dp[i-1][0][0]+dis[c[i-1]][c[i]],j=1;j<=min(i,m);++j)
	{
		dp[i][j][0]=min(dp[i][j][0],min(dp[i-1][j][0]+dis[c[i-1]][c[i]],dp[i-1][j][1]+dis[c[i-1]][c[i]]*(1-k[i-1])+dis[d[i-1]][c[i]]*k[i-1]));
		dp[i][j][1]=min(dp[i][j][1],min(dp[i-1][j-1][0]+dis[c[i-1]][c[i]]*(1-k[i])+dis[c[i-1]][d[i]]*k[i],dp[i-1][j-1][1]+dis[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i])+dis[c[i-1]][d[i]]*(1-k[i-1])*k[i]+dis[d[i-1]][c[i]]*k[i-1]*(1-k[i])+dis[d[i-1]][d[i]]*k[i-1]*k[i]));
	}
	for(int i=0;i<=m;++i)ans=min(ans,min(dp[n][i][0],dp[n][i][1]));
	printf("%.2lf",ans);
}
int main(){in(),ac();}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章