POJ 2671 Jimmy's Bad Day題解(很詳細很友好,相似區間dp)

<font size=3>ios

有問題的話歡迎在評論區提出c++

##題意: 題目連接 你是一個送快遞的,如今給你一個環,環的邊有權值,表明走這條邊所花的時間,每一個點表明一個地點,點有點權,表明這個點上有多少貨物須要你送。初始時間$t=0$,每到一個點,你就能夠瞬間送完該點全部的貨物,但每個貨物都會給你帶來值爲當前時間的罰款。如今你要送完全部貨物,問最優狀況下你的罰款最少是多少。 ##題解: 從樣例能夠看出,這題的核心就在於,快遞員能夠「反覆橫跳」,好比能夠先逆時針送完一個點的貨物,再掉頭沿順時針方向送完全部其它點的貨物(儘管這樣使得一條邊通過了兩次,但使得總罰款更少了)。函數

爲了方便下面的說明,先約定些定義:我把起點記作0號點,0號點逆時針走$j$格到達的點叫作「逆時針第$j$點」,0號點順時針走$i$格到達的點叫作「順時針第$i$點」;spa

例如,下圖中,4號點是順時針第4點與逆時針第2點。.net

廢話很少說,直接看狀態的定義: $dp[i][j][0]$:若是$t=0$的時候你站在順時針$i$號點,送完包括端點在內,整個$[i,j]$區間的最小罰款; $dp[i][j][1]$:若是$t=0$的時候你站在逆時針$j$號點,送完包括端點在內,整個$[i,j]$區間的最小罰款;code

有兩點值得注意的:blog

  • 狀態定義中假設了$t=0$時的位置,但事實上$t=0$的時候你是在起點的。爲什麼這麼假設呢?往下看就知道了;
  • 上述所謂$[i,j]$區間,表示的是由i順時針到j的區間(劃重點,也就是說,該區間走的是不含起點的那條路!),這裏爲了便於理解說成是$[i,j]$,下面也用這種方式表示區間和點,不要搞混了。

那麼仍如下圖爲例,取$i=1$和$j=1$,看看如何更新;get

再次強調,該圖裏的$[i,j]$區間是$1,2,3,4,5$這一段string

$dp[i][j][0]$能夠經過以下兩種方式更新(特別提醒,因爲$t=0$時你就在順時針第$i$個位置,因此$i$處的貨物不帶來任何罰款,這也是下面兩種狀況中咱們都無視了$i$點罰款的緣由):io

  • 走到$i+1$,這樣作的花費是 : $dp[i+1][j][0]+$( 區間$[i+1,j]$的貨物數量之和 $*i$與$i+1$點間的時間 ) 解釋:$dp[i][j][0]$假設$t=0$的時候你在$i$處,而$dp[i+1][j][0]$的$t=0$時刻假設你在$i+1$處,所以,二者的「開始時間」有個差距,即「$i$到$i+1$的時間」,該時間的影響是平均的做用在該區間全部貨物上的。
  • 掉頭走到$j$,這樣作的花費是 : $dp[i+1][j][1]+$( 區間$[i+1,j]$的貨物數量之和 $*i$掉頭走到$j$點的時間 ) 解釋:與上面的狀況對比,仍然是$[i+1,j]$中的全部貨物都統一的被拖延了一個時間,只不過此次該時間變成了$i$到$j$的一條路的時間

最終,$dp[i][j][0]$的值就取這二者與本身之間的最小值,就能夠完成更新。

$dp[i][j][1]$的更新同理,能夠參考下面dfs函數內寫的方法。

##後記:

最開始,我定義的$dp[i][j][0]$是「由$i$逆時針到$j$所有送完的最小罰款」,也就是走含起點的那一條路,相信應該會有一些人第一反應也這麼想吧。但這樣搞的話,彷佛就無法轉移了,由於你無法處理掉頭屢次的狀況。 總之,第一反應能想到一個正確的狀態,真的很重要。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int inf=0x3f3f3f3f;
int n,num[310],len[310],lpre[310],npre[310];
int dp[310][310][2];
int nsum(int i,int j){
	int tmp=(i!=0);j=n-j;
	return npre[j]-npre[i-tmp];
}
int lsum(int i,int j){
	return lpre[j]-lpre[i];
}
int dfs(int i,int j,int pos){
	if(dp[i][j][pos]!=inf){
		return dp[i][j][pos];
	}
	if(i+j>=n){
		return dp[i][j][pos]=0;
	}
	if(pos==1){
		dp[i][j][1]=min(dp[i][j][1],dfs(i,j+1,1)+len[n-(j+1)]*nsum(i,j+1));
		dp[i][j][1]=min(dp[i][j][1],dfs(i,j+1,0)+(lpre[i]+lsum(n-j,n))*nsum(i,j+1));
	}
	else{
		dp[i][j][0]=min(dp[i][j][0],dfs(i+1,j,0)+len[i]*nsum(i+1,j));
		dp[i][j][0]=min(dp[i][j][0],dfs(i+1,j,1)+(lpre[i]+lsum(n-j,n))*nsum(i+1,j));
	}
	return dp[i][j][pos];
}
int main(){
	while(~scanf("%d",&n)){
		if(!n)	break;
		for(int i=0;i<n;i++){
			scanf("%d%d",&num[i],&len[i]);
		}
		lpre[0]=npre[0]=num[n]=0;
		for(int i=1;i<=n;i++){
			lpre[i]=lpre[i-1]+len[i-1];
			npre[i]=npre[i-1]+num[i];
		}
		memset(dp,inf,sizeof(dp));
		int ans=min(dfs(0,0,0),dfs(0,0,1));
		printf("%d\n",ans);
	}
	return 0;
}

</font>

相關文章
相關標籤/搜索