1. 程式人生 > 實用技巧 >[Noip2016]換教室

[Noip2016]換教室

[Noip2016]換教室

一.前言

​ 阿巴阿巴,好長的題面……題目連結

二.思路

​ 看到數學期望就基本知道是DP了,然後狀態也比較好想,屬於最基礎的那種 \(f_{ij}\) 表示前 i 個共提交了 j 次申請,但是涉及到申請的概率會交叉的問題,於是再加一維 0/1 表示這次是否嘗試

​ 這裡多嘴一句,一開始我還以為是總共只能成功 m 次,就把方程設成了 j 次成功……然後後面 0/1 那一維也是不可以設成成功的。(可以自己試試,反正我不行)

​ 然後開始轉移,對於當前情況,有(dis 表示教室之間的距離)

\[f[i][j][0]=\text{min}(dp[i-1][j][0]+dis[c[i-1]][c[i]],\\ dp[i-1][j][1]+(1.0-k[i-1])*dis[c[i-1]][c[i]]+k[i-1]*dis[d[i-1]][c[i]]) \]

就稍微解釋……這次沒有嘗試,則上一次就要嘗試 j 次,然後由上次沒嘗試和嘗試了的成功與否轉移。同理,

\[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]+ k[i-1]*k[i]*dis[d[i-1]][d[i]]+ (1.0-k[i-1])*k[i]*dis[c[i-1]][d[i]]+\\ k[i-1]*(1.0-k[i])*dis[d[i-1]][c[i]]+ (1.0-k[i-1])*(1.0-k[i])*dis[c[i-1]][c[i]] ) \]

感覺有點過於長了……跟上面相同的思想我就不講了。

​ 最後是關於 dis ,看資料範圍,教室數小於300 ,Floyd蕪湖起飛~ (鬼知道我白給一個小時打了個什麼奇怪演算法呢~)

​ 就醬。

三.CODE

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<fstream>
#include<cmath>
#include<cstring>
using namespace std;
#define m_for(i,a,b) for(int i=a;i<=b;++i)
int read(){
	char ch=getchar();
	int res=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())res=res*10+(ch-'0');
	return res*f;
}
const int MAXN=90005;
int n,m,v,e;
int c[MAXN],d[MAXN],dis[305][305];
double p[2005][2005][2],dp[2005][2005][2];
double k[MAXN];
int main(){
	memset(dis,127/3,sizeof(dis));
	n=read();m=read();v=read();e=read();
	m=min(m,n);
	m_for(i,1,n)c[i]=read();
	m_for(i,1,n)d[i]=read();
	m_for(i,1,n)scanf("%lf",&k[i]);
	m_for(i,1,v)dis[i][0]=dis[0][i]=dis[i][i]=0.0;
	for(int i=1,a,b,w;i<=e;++i){
		a=read();b=read();w=read();
		dis[a][b]=dis[b][a]=min(dis[b][a],w);
	}
	m_for(a,1,v)m_for(b,1,v)m_for(w,1,v)
	dis[b][w]=min(dis[b][w],dis[b][a]+dis[a][w]);
	m_for(i,1,n)m_for(j,0,m)dp[i][j][0]=dp[i][j][1]=MAXN*100.0;
	dp[1][1][1]=dp[1][0][0]=0.0;
	m_for(i,2,n){
		dp[i][0][0]=dp[i-1][0][0]+dis[c[i-1]][c[i]];
		for(int j=1;j<=i&&j<=m;++j){
			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]+
			k[i-1]*k[i]*dis[d[i-1]][d[i]]+
			(1.0-k[i-1])*k[i]*dis[c[i-1]][d[i]]+
			k[i-1]*(1.0-k[i])*dis[d[i-1]][c[i]]+
			(1.0-k[i-1])*(1.0-k[i])*dis[c[i-1]][c[i]]
			);
			dp[i][j][0]=min(
			dp[i-1][j][0]+dis[c[i-1]][c[i]],
			dp[i-1][j][1]+(1.0-k[i-1])*dis[c[i-1]][c[i]]+k[i-1]*dis[d[i-1]][c[i]]
			);
		}
	}
	double ans=MAXN*100.0;
	for(int i=0;i<=m;++i){
		ans=min(ans,min(dp[n][i][0],dp[n][i][1]));
	}
	printf("%.2lf",ans);
	return 0;
}