兩道DP,四年修一次路

2022-03-06 19:42:42 字數 3785 閱讀 8065

題目描述

snj位於hb省西部一片群峰聳立的高大山地,橫亙於a江、b水之間,方圓數千平方公里,相傳上古的神醫在此搭架上山採藥而得名。景區山峰均在海拔3000公尺以上,堪稱"華中屋脊"。snj是以秀綠的亞高山自然風光,多樣的動植物種,人與自然和諧共存為主題的森林生態區。

snj處於中國地勢第二階梯的東部邊緣,由大巴山脈東延的餘脈組成中高山地貌,區內山體高大,高低不平。 交通十分不便。

最近,hb省決定修一條從yc市通往snj風景區的高速公路。經過勘測分析,途中需要經過高度分別為h1,h2,……,hn的n個山區。由於高低不平,除正常的修路開支外,每段還要多出高度差|hi - hi-1|*x萬元的斜坡費用。dr. kong 決定通過填高一些區域的高度來降低總的費用。當然填高也是需要一些費用的。每填高y單位,需要付出y2萬元費用。

你能否幫dr. kong做出乙個規劃,通過部分填高工程改造,使得總的費用降下來。

第一行: t 表示以下有t組測試資料( 1≤ t ≤8 )

對每組測試資料,

第一行:n x(2 ≤ n ≤100,000 1≤ x ≤100)

第二行:n個整數,分別表示n個區域的高度hi( 1<=hi<=100 , i=1…. n)

對每組測試資料,輸出佔一行,乙個整數,即經過部分填高工程改造後的最少費用。

dp[i][j]表示 第i個山區 在第j高度下時,所需要的最少費用

狀態轉移方程:dp[i][j] = min( dp[i-1][k] + 第i個山區填高的費用 + 第i-1和第i個山區斜坡的花費)

為什麼可以這樣轉移?

因為 題目給出的資料 區域最大高度不過100,而山區最大由100000個,借助資料特點。

#includeusing namespace std;

const int maxn = 100005;

int t;

int a[maxn];

int dp[maxn][210];

int n,x;

const int inf = 0x3f3f3f3f;

int main()

memset(dp,inf,sizeof(dp));

for(int i=a[1];i<=100;i++)

for(int i=2;i<=n;i++)

}} int ans = inf;

for(int i=1;i<=100;i++)

cout題目描述

某山區的孩子們上學必須經過一條凹凸不平的土路,每當下雨天,孩子們非常艱難。現在村里走出來的dr. kong決定募捐資金重新修建著條路。由於資金有限,為了降低成本,對修好後的路面高度只能做到單調上公升或單調下降。

為了便於修路,我們將整個土路分成了n段,每段路面的高度分別a1,a2,….,an。由於將每一段路墊高或挖低乙個單位的花費成本相同,修路的總費用與路面的高低成正比。

現在dr. kong希望找到乙個恰好含n個元素的不上公升或不下降序列b1,b2,….,bn,作為修過的路路段的高度。要求:

| a1-b1| + | a2–b2| + ... + | an-bn|------>最小

參考部落格:

這道題和第十一屆的類似,只不過dp轉移需要改變;

因為ai 最大 有1e9,我們不能再用dp[i]j,這裡j最大有1e9,這種方法不可行

而題目給出的資料 ai 雖然大,但是 n小啊。

這道題的正確做法:dp[i][j] 表示第i段在第a[j]高度下的最小費用,前i-1段已經修好了,為什麼可以這樣?因為題目要求最小花費,我們最大也只能修到和 a[j]陣列的最大值相等的高度,也就是a[j]

也就是 dp[i][j]:= 把第i段路修成b[j]高度,所需要的最小花費(這時,前i-1段路都已經搞定啦)

由於是不遞減,所以第i段路修成b[j],那麼第i-1段路就只能修成b[1]-b[j],要求第i段路的最優解,則求:

dp[i][j]=min(dp[i-1][1],dp[i-1][2]……dp[i-1][j])+abs(a[i]-b[j])

第i-1段路修成b[1],b[2]…b[j]的最小值,加上第i段路修成b[j]所需要的消費

#include#include#includeusing namespace std;

int a[505],b[505],n,dp[505][505];

const int inf=0x3f3f3f3f;

int cmp(int a,int b)

int solve()

for(int i=2;i<=n;i++)

}int ans=inf;

for(int i=1;i<=n;i++)

return ans;}

int main()

sort(b+1,b+1+n);

int ans1=solve();

sort(b+1,b+1+n,cmp);

int ans2=solve();

printf("%d\n",min(ans1,ans2));}}

o(n^3)

o(n^3)的做法 更為直觀,但是執行了1700ms會超時。

dp[i][j]表示 第i個點 在高度為b[j]下的最小花費

狀態轉移方程:第i個點 在第b[j]高度 = min(第i-1個點,在第b[k]高度)

所以三層迴圈直接遞推。

#includeusing namespace std;

const int maxn = 510;

int t;

int n;

int dp[maxn][maxn][2];

int a[maxn];

int b[maxn];

const int inf = 0x3f3f3f3f;

int main()

sort(b+1,b+n+1);

memset(dp,inf,sizeof(dp));

for(int i=1;i<=n;i++)

//第一次dp 正序

for(int i=2;i<=n;i++)}}

//第二次 這裡超時 k從j~n 最壞時間複雜度o(n^3)

/* for(int i=2;i<=n;i++)}}

*/ reverse(a+1,a+n+1);

for(int i=1;i<=n;i++)

//把陣列反轉重新 再進行一次第一次dp

for(int i=2;i<=n;i++)}}

int ans = inf;

for(int i=0;i<=n;i++)

cout本題小結與想法:

從o(n^3) 優化到o(n^2)的一些理解:

o(n^2)利用單調不減性質:tmp求出了 把第i-1段路修成不超過b[j]的最小花費,tmp找出的是(i-1段路修成不超過b[j]的最小花費)的最優解,邊推(i從2~n的過程中)邊更新這個最優解,以用來求第i段路修成不超過b[j]的最小花費

o(n3):每次j變化時,都從0開始到j結束找一遍i-1段路修成不超過b[j]的最小花費,可以優化成o(n2)邊推邊更新。

兩道區間DP水題

區間dp,如果不考慮演算法之間的轉化,那麼就是很簡單的,只是邊界不好處理,但用記憶化搜尋就不存在這樣的問題了 方程 f l r max f l r f l k f k 1 r he ad l he ad k 1 head r 1 f l r max f l r f l k f k 1 r head ...

兩道NOIP裡的DP題目

某國為了防禦敵國的飛彈襲擊,發展出一種飛彈攔截系統。但是這種飛彈攔截系統有乙個缺陷 雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的飛彈來襲。由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的飛彈。輸入格式 輸入資料為兩行,第一行...

2023年3月5日CVTE網測的兩道程式設計題

昨天,也就是2016年3月5日晚上19 00 20 30,這是今年cvte春招的網測時間,博主沒能趕上時間,從舍友裡摘取了面試安卓開發的兩道網測程式設計題,在這裡,博主要用c語言來實現它。第一道題 字元陣列的迴圈右移問題 題目要求 將n個字元的陣列,迴圈右移k位。時間複雜度o n 思路分析 1 含n...