<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>