1. 程式人生 > >【區間dp*2】洛谷 P1880 [NOI1995]石子合併 (推導過程) +尼克的任務(還沒寫)

【區間dp*2】洛谷 P1880 [NOI1995]石子合併 (推導過程) +尼克的任務(還沒寫)

emmmmm給自己設定了一個習慣界限。其實每次看到他們在做啥啥啥而我都大三都現在了  就會感覺很內傷-、-

不過演算法還是要寫的吧 別寫太多而已...

為了防止腦袋空空 也為了防止一天不知道幹什麼

==================================

 

石子合併:

經典 的區間dp題目

===============

然後呢,基本其實也就兩種模型-----括號一種,石子一種……

之前的括號合併+石子以前寫過:https://blog.csdn.net/StrongerIrene/article/details/81944120?utm_source=blogxgwz7

但是我的版本並不能輕易讓人看懂,所以還是去看了紫書的dp部分。

總結一下就是:(基於紫書的講解來)

題目是給木棍,3個可以切的位置,10米長,2 4 7  每次切到的花費是你當前切的木棍的長度 問怎麼切會使得切得費用最少?

(按照2 4 7  就是10+ (8left) 8+(6left)6+ 

 4 2 7 則是  10+(6  4  left)4+ (2 2 6 left) 6+ (2 2 3 3left) )

下面的4 2 7 是更好的。我們前面還記得動態規劃的時候,有一個特點是。任何地方停下來都是最好的

而另一個題目是,石子合併,代價是石子的塊數(數目)(NOI1995 在落谷上面)

可是呢?

就石子而言,可以得到,最後一次的肯定是最好的,那前面呢?最後石子的和是22,22可以是由  18+4  4+18  9+13得來的。(當時其實想到了,但是不確定哪個方向來的最好-------------與其不知道,複雜度也夠了的情況下,不如這個方向檢驗一下,這樣就得到了我們的區間dp策略。

那麼再回到小木棍。

這個狀態怎麼說呢,有一點帶權值的感覺。所得到的值是和分片劃分又糅合起來的感覺一樣的。。每個狀態裡搞出來的不僅僅是dp(i,j)...相加,還有此次運算搞出來的東西,找找專業說法是什麼。。

區間DP主要是把一個大區間拆分成幾個小區間,先求小區間的最優值,然後合併起來求大區間的最優值。

區間DP最關鍵的就是滿足最優子結構以及無後效性!!!

對於這個洛谷題目我還是嘗試下寫一寫吧。畢竟屬於複習的內容。。。。(不知道自己現在學演算法為了什麼。。。PAT+CCF明明就不一樣吧-。-)啊,為了看上去還沒那麼菜,要變強還要學這個流那個流什麼的一堆東西。。。 還有就是自己喜歡,刷完洛谷100題起碼不會和沒搞過的人完全一樣吧。

 

最後A了!!!!!!!!!!!

在孜傑的鞭笞幫助下

發現了,我很容易寫一些小錯誤~

比如minn  maxx什麼的

另外還有,對於兩個差不多的(我的就是dp1和dp2  改了不同步的地方最好直接複製~因為不同步,往往只是改了一邊兒。或者不用複製,直接寫在一個dp(。。。)裡面。)

小錯誤不斷:比如區間最大值 

這個範圍,不能越界,也不能漏掉!

拿2--4來說:

2/2   3 /4 

2/3   4/4  這個呢 範圍也就是   

(按照程式碼來: )t=dp(.....)  ←這個i是x到y-1的

(按照程式碼來: )t=dp(,...)← 這個i+1是x+1到y的  (所以,最開始寫i<=y 那麼Y+1到y會錯啊

因為是分成兩塊組成的嘛。不能等於y否則下面就dp(5,4)什麼的了。總之多寫吧emmmm

(但是其實可以自己事先覺察到的)

 //我不知道她的話是不是帶有鄙視成分 我亦不知道我能做成什麼樣子
//總之日常保持演算法洛谷的能力總歸沒錯!!!!
//不然搞其他都是虛的!!可不要小看系主任!!!!
#include <bits/stdc++.h>
using namespace std;
int n;
int a[205];
int d[205][205];
int d2[205][205];
int cal(int i,int j){
	int sum=0;
	for(int k=i;k<=j;k++){
		sum+=a[k];
	}
	return sum;
}
int dp(int x, int y){
	int maxx=0;
	if(x==y)return 0;//!!!!hope this never appear!!!!
	if(y-x==1)return (a[x]+a[y]);
	if(d[x][y]!=0)return d[x][y];
	int t=0;
	int sum=cal(x,y);
	//x==1 y==2
	//i=2 i<2 never comes into
	//y=3  comes once
	for(int i=x;i<y;i++){
		t=dp(x,i);
		t+=dp(i+1,y);
		maxx=max(t,maxx);
	}
	maxx+=sum;
	d[x][y]=maxx;
	//printf(" x is %d y is %d maxx is %d \n",x,y,maxx);
	return maxx;
}
int dp2(int x, int y){
	int maxx=999999999;
	if(x==y)return 0;//!!!!hope this never appear!!!!
	if(y-x==1)return (a[x]+a[y]);
	if(d2[x][y]!=0)return d2[x][y];
	int t=0;
	int sum=cal(x,y);
	for(int i=x;i<y;i++){
		t=dp2(x,i);
		t+=dp2(i+1,y);
		maxx=min(t,maxx);
	}
	maxx+=sum;
	d2[x][y]=maxx;
	//printf(" x is %d y is %d maxx is %d \n",x,y,maxx);
	return maxx;
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i+n]=a[i];
	}
	dp(1,n);
	//一共有n+1種方法
	//i=0  1--n
	//i=n  1+n--2*n
	int maxxx=-1;
	for(int i=0;i<=n;i++){
		maxxx=max(maxxx,dp(1+i,n+i));
	}
	int minnn=999999999;
	for(int i=0;i<=n;i++){
		minnn=min(minnn,dp2(1+i,n+i));
	}
	cout<<minnn<<endl;
	cout<<maxxx<<endl;
    return 0;
}

 

另外,之前一道“會壞掉的項鍊”題目中,如果對於環形。。。。 我們有一個解決方案,那就是區間加長一倍。其中只要控制一下區間長度就好了(不超過xxx)對於這個題就是dp多寫一點。。

 

 @四邊形不等式演算法優化了一維。使得它從三次方降級到了平方(先別管。。)

@剛才看了一個人的部落格。。。。

哎 總不能不學習  哪怕你說什麼藉口不喜歡啊不想啊

按照番茄todo上的計劃每天一步一步來吧。

(小日常app上,每天完成todo任務+每天做了計劃,就打卡。)

===========尼克的任務===========