題目描述
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...