1. 程式人生 > >洛谷P1850 換教室

洛谷P1850 換教室

不知道 msu scan memset scrip ios ring 輸出格式 提交

題目描述

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

在可以選擇的課程中,有 2n2n 節課程安排在 nn 個時間段上。在第 ii(1 \leq i \leq n1in)個時間段上,兩節內容相同的課程同時在不同的地點進行,其中,牛牛預先被安排在教室 c_ic?i?? 上課,而另一節課程在教室 d_id?i?? 進行。

在不提交任何申請的情況下,學生們需要按時間段的順序依次完成所有的 nn 節安排好的課程。如果學生想更換第 ii 節課程的教室,則需要提出申請。若申請通過,學生就可以在第 ii 個時間段去教室 d_id?i?? 上課,否則仍然在教室 c_ic?i?? 上課。

由於更換教室的需求太多,申請不一定能獲得通過。通過計算,牛牛發現申請更換第 ii 節課程的教室時,申請被通過的概率是一個已知的實數 k_ik?i??,並且對於不同課程的申請,被通過的概率是互相獨立的。

學校規定,所有的申請只能在學期開始前一次性提交,並且每個人只能選擇至多 mm 節課程進行申請。這意味著牛牛必須一次性決定是否申請更換每節課的教室,而不能根據某些課程的申請結果來決定其他課程是否申請;牛牛可以申請自己最希望更換教室的 mm 門課程,也可以不用完這 mm 個申請的機會,甚至可以一門課程都不申請。

因為不同的課程可能會被安排在不同的教室進行,所以牛牛需要利用課間時間從一間教室趕到另一間教室。

牛牛所在的大學有 vv 個教室,有 ee 條道路。每條道路連接兩間教室,並且是可以雙向通行的。由於道路的長度和擁堵程度不同,通過不同的道路耗費的體力可能會有所不同。 當第 ii(1 \leq i \leq n-11in?1)節課結束後,牛牛就會從這節課的教室出發,選擇一條耗費體力最少的路徑前往下一節課的教室。

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

輸入輸出格式

輸入格式:

第一行四個整數 n,m,v,en,m,v,e。nn 表示這個學期內的時間段的數量;mm 表示牛牛最多可以申請更換多少節課程的教室;vv 表示牛牛學校裏教室的數量;ee表示牛牛的學校裏道路的數量。

第二行 nn 個正整數,第 ii(1 \leq i \leq n1in)個正整數表示 c_ic?i??,即第 ii 個時間段牛牛被安排上課的教室;保證 1 \le c_i \le v1c?i??v。

第三行 nn 個正整數,第 ii(1 \leq i \leq n1in)個正整數表示 d_id?i??,即第 ii 個時間段另一間上同樣課程的教室;保證 1 \le d_i \le v1d?i??v。

第四行 nn 個實數,第 ii(1 \leq i \leq n1in)個實數表示 k_ik?i??,即牛牛申請在第 ii 個時間段更換教室獲得通過的概率。保證 0 \le k_i \le 10k?i??1。

接下來 ee 行,每行三個正整數 a_j, b_j, w_ja?j??,b?j??,w?j??,表示有一條雙向道路連接教室 a_j, b_ja?j??,b?j??,通過這條道路需要耗費的體力值是 w_jw?j??;保證 1 \le a_j, b_j \le v1a?j??,b?j??v, 1 \le w_j \le 1001w?j??100。

保證 1 \leq n \leq 20001n2000,0 \leq m \leq 20000m2000,1 \leq v \leq 3001v300,0 \leq e \leq 900000e90000。

保證通過學校裏的道路,從任何一間教室出發,都能到達其他所有的教室。

保證輸入的實數最多包含 33 位小數。

輸出格式:

輸出一行,包含一個實數,四舍五入精確到小數點後恰好22位,表示答案。你的輸出必須和標準輸出完全一樣才算正確。

測試數據保證四舍五入後的答案和準確答案的差的絕對值不大於 4 \times 10^{-3}4×10??3??。 (如果你不知道什麽是浮點誤差,這段話可以理解為:對於大多數的算法,你可以正常地使用浮點數類型而不用對它進行特殊的處理)

輸入輸出樣例

輸入樣例#1:
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
輸出樣例#1:
2.80

說明

【樣例1說明】

所有可行的申請方案和期望收益如下表:

技術分享

【提示】

  1. 道路中可能會有多條雙向道路連接相同的兩間教室。 也有可能有道路兩端連接

的是同一間教室。

2.請註意區分n,m,v,e的意義, n不是教室的數量, m不是道路的數量。

技術分享

特殊性質1:圖上任意兩點 a_ia?i??, b_ib?i??, a_ia?i??≠ b_ib?i??間,存在一條耗費體力最少的路徑只包含一條道路。

特殊性質2:對於所有的 $1≤ i≤ n$, k_i= 1k?i??=1 。

483 055 310

題解:floyed+dp

代碼:

68分暴力...開心......

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,v,e;
int c[20009],d[20009],dis[3002][3020];
double ans,k[20009];
int main(){
    scanf("%d%d%d%d",&n,&m,&v,&e);
    /*n表示時間段的數量,m為最多可以申請多少
    v表示學校裏教室的數量,e表示學校裏道路的數量。 
    */
    memset(dis,0x3f,sizeof(dis));
    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;i<=v;i++)dis[i][i]=0;
    for(int i=1;i<=e;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        dis[x][y]=dis[y][x]=min(dis[x][y],z);
    } 
    for(int kk=1;kk<=v;kk++)
     for(int i=1;i<=v;i++)
      for(int j=1;j<=v;j++)
       dis[i][j]=min(dis[i][j],dis[i][kk]+dis[kk][j]);
    if(m==0){
        for(int i=1;i<n;i++)ans+=dis[c[i]][c[i+1]]*1.0;
        printf("%.2lf",ans);
        return 0;
    }
    if(m==1){
        dis[0][d[1]]=0;dis[0][c[1]]=0;
        dis[d[n]][c[n+1]]=0;dis[c[n]][c[n+1]]=0;
        for(int i=1;i<n;i++)ans+=1.0*dis[c[i]][c[i+1]];
        double len=ans;
        for(int i=1;i<=n;i++){
            double tmp=0.0;
            tmp+=(1.0-k[i])*len;
            tmp+=k[i]*(len-dis[c[i-1]][c[i]]-dis[c[i]][c[i+1]]+dis[c[i-1]][d[i]]+dis[d[i]][c[i+1]]);
            ans=ans<tmp?ans:tmp; 
        }
        printf("%.2lf\n",ans);
        return 0;
    }
    if(m==2){
        dis[0][d[1]]=0;dis[0][c[1]]=0;
        dis[d[n]][c[n+1]]=0;dis[c[n]][c[n+1]]=0;
        for(int i=1;i<n;i++)ans+=1.0*dis[c[i]][c[i+1]];
        double len=ans;
        for(int i=1;i<=n;i++){
            double tmp=0.0;
            tmp+=(1.0-k[i])*len;
            tmp+=k[i]*(len-dis[c[i-1]][c[i]]-dis[c[i]][c[i+1]]+dis[c[i-1]][d[i]]+dis[d[i]][c[i+1]]);
            ans=ans<tmp?ans:tmp; 
        }
        
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                double tmp=0.0;
                tmp+=(1-k[i])*(1-k[j])*len;
                tmp+=k[i]*k[j]*(len-dis[c[i-1]][c[i]]-dis[c[i]][c[i+1]]+dis[c[i-1]][d[i]]+dis[d[i]][c[i+1]]-dis[c[j-1]][c[j]]-dis[c[j]][c[j+1]]+dis[c[j-1]][d[j]]+dis[d[j]][c[j+1]]);
                tmp+=(1-k[i])*k[j]*(len-dis[c[j-1]][c[j]]-dis[c[j]][c[j+1]]+dis[c[j-1]][d[j]]+dis[d[j]][c[j+1]]);
                tmp+=(1-k[j])*k[i]*(len-dis[c[i-1]][c[i]]-dis[c[i]][c[i+1]]+dis[c[i-1]][d[i]]+dis[d[i]][c[i+1]]);
                ans=ans<tmp?ans:tmp; 
            }
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}

正解:以後floyed還是循環賦值好了....免得把應該為0的設為Inf

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int n,m,v,e;
int dis[2017][2017],c[2017],d[2017];
double k[2100],dp[2017][2017][3];

int main(){
    scanf("%d%d%d%d",&n,&m,&v,&e);
    /* 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;i<=v;i++){
    for(int j=1;j<=v;j++)
         dis[i][j]=0x3f3f3f3f;
         dis[i][i]=0;
    }
    for(int i=1;i<=v;i++)dis[i][i]=0;
    for(int i=1;i<=e;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        dis[x][y]=dis[y][x]=min(dis[x][y],z);
    }
    for(int kk=1;kk<=v;kk++)
     for(int i=1;i<=v;i++)
      for(int j=1;j<=v;j++)
       dis[i][j]=min(dis[i][j],dis[i][kk]+dis[kk][j]);
    for(int i=1;i<=n;i++){
        dp[i][0][0]=dp[i-1][0][0]+dis[c[i-1]][c[i]];
        dp[i][0][1]=1000000.0;
        for(int j=1;j<=m;j++){
            dp[i][j][0]=min(dp[i-1][j][0]+dis[c[i-1]][c[i]],
            dp[i-1][j][1]+k[i-1]*dis[d[i-1]][c[i]]+(1.0-k[i-1])*dis[c[i-1]][c[i]]);
            dp[i][j][1]=min(dp[i-1][j-1][0]+k[i]*dis[c[i-1]][d[i]]+(1.0-k[i])*dis[c[i-1]][c[i]],
            dp[i-1][j-1][1]+(1.0-k[i-1])*(1.0-k[i])*dis[c[i-1]][c[i]]+k[i]*k[i-1]*dis[d[i-1]][d[i]]+k[i-1]*(1.0-k[i])*dis[d[i-1]][c[i]]+(1.0-k[i-1])*k[i]*dis[c[i-1]][d[i]]
            );
        }
    }
    printf("%.2f",min(dp[n][m][0],dp[n][m][1]));
    return 0;
}

洛谷P1850 換教室