SMZX夏令營10日遊(第一階段DP):石子歸併,小A點菜,All in All,瘋狂的採藥程式碼詳解
石子歸併 經典的區間DP
題目描述
在一個圓形操場的四周擺放 \(\rm N\) 堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。
試設計出一個演算法,計算出將 \(\rm N\) 堆石子合併成 \(1\) 堆的最小得分和最大得分。
輸入格式
資料的第\(1\)行是正整數 \(\rm N\),表示有 \(\rm N\) 堆石子。
第\(2\)行有\(\rm N\)個整數,第\(\rm i\)個整數\(a_i\)表示第\(i\rm\)堆石子的個數。
輸出格式
輸出共 \(2\) 行,第 \(1\) 行為最小得分,第 \(2\)
輸入輸出樣例
輸入 #1
\(4\)
\(4\) \(5\) \(9\) \(4\)
輸出 #1
\(43\)
\(54\)
說明/提示
\(1\) \(≤\) \(\rm N\) \(≤\) \(100\),\(0\) \(≤\) \(a_i\) \(≤\) \(20\)。
Code
#include<bits/stdc++.h> //萬能頭 using namespace std; int n,f2[1006][1006],f1[1006][1006],num[1006],s[1006],maxx,minn;//定義 int sum(int i,int j)//計算i~j石子的數量 { return s[j]-s[i-1]; } int main() { cin>>n;//普普通通的輸入 for(int i=1;i<=2*n;i++)//2倍拆環。注:測試樣例最好用freopen或洛谷的線上IDE,否則手動輸入樣例你會發現不得了的東西…… { cin>>num[i];//普普通通的輸入+1 num[i+n]=num[i];//多copy一個環 s[i]=s[i-1]+num[i];//字首和 } for(int p=1;p<n;p++)//防止越界 { for(int i=1,j=p+i;(j<2*n) && (i<2*n);i++,j=i+p)//設定i~jDP範圍,i是左邊,就是右邊 { f1[i][j]=-1;//求最大值設最小值 f2[i][j]=1e9;//同上,求最小值設最大值 for(int k=i;k<j;k++)//列舉分割點 { f1[i][j]=max(f1[i][j],f1[i][k]+f1[k+1][j]+sum(i,j));//動態轉移方程,f1[i][j]為不選,f1[i][k]為i~k的合併得到的得分,f1[k+1][j]同上,k+1~j的合併得到的得分 f2[i][j]=min(f2[i][j],f2[i][k]+f2[k+1][j]+sum(i,j));//同上,不在囉嗦 } } } maxx=-1;//求最大值設最小值 minn=1e9;//同上,求最小值設最大值 for(int i=1;i<=n;i++) maxx=max(maxx,f1[i][i+n-1]);//在f1這裡找最大,本處用的是打擂臺 for(int i=1;i<=n;i++) minn=min(minn,f2[i][i+n-1]);//同上,在f2裡找最小 cout<<minn<<endl<<maxx;//輸出 return 0; //完結撒花 }
小A點菜 非常簡單的01揹包
題目背景
\(\rm uim\)神犇拿到了\(\rm uoi\)的\(\rm ra\)(鐳牌)後,立刻拉著基友小\(\rm A\)到了一家餐館,很低端的那種。
\(\rm uim\)指著牆上的價目表(太低階了沒有選單),說:“隨便點”。
題目描述
不過\(\rm uim\)由於買了一些輔\(\rm (e)\)輔\(\rm (ro)\)書,口袋裡只剩\(\rm M\)元(M≤10000)。
餐館雖低端,但是菜品種類不少,有\(\rm N\)種(N≤100),第\(\rm i\)種賣\(\rm a_i\)元\(\rm (a_i ≤1000)\)。
由於是很低端的餐館,所以每種菜只有一份。
小\(\rm A\)
由於小\(\rm A\)肚子太餓,所以最多隻能等待\(1\)秒。
輸入格式
第一行是兩個數字,表示\(\rm N\)和\(\rm M\)。
第二行起\(\rm N\)個正數\(a_i\)
(可以有相同的數字,每個數字均在\(1000\)以內)。
輸出格式
一個正整數,表示點菜方案數,保證答案的範圍在int之內。
輸入輸出樣例
輸入 #1
\(4\) \(4\)
\(1\) \(1\) \(2\) \(2\)
輸出 #1
\(3\)
Code
//小A點菜(01揹包) (選與不選)
#include<bits/stdc++.h>//萬能頭
using namespace std;
long long m,f[10000000],W,w[10000],v[10000];//定義
int main()
{
cin>>m>>W;//普普通通的輸入
for(int i=1;i<=m;i++)
cin>>w[i];
f[0]=1;//初始化(小心爆0,之所以設定1,是因為當沒有菜時,只能不選)
for(int i=1;i<=m;i++)
for(int j=W;j>=w[i];j--)//01揹包標準模板
f[j]+=f[j-w[i]];//(累加)
cout<<f[W];
return 0;//完結撒花
}
All in All (無DP版,指標版本)
題目描述
讀入兩個字串s和t,問是否能通過刪去串t中的某幾個字元得到串s,(大小寫區分),如果能則輸出 Yes,否則輸出 No。
輸入輸出樣例
輸入 #1
\(\rm sequence subsequence\)
\(\rm person compression\)
\(\rm VERDI vivaVittorioEmanueleReDiItalia\)
\(\rm caseDoesMatter CaseDoesMatter\)
輸出 #1
\(\rm Yes\)
\(\rm No\)
\(\rm Yes\)
\(\rm No\)
Code
#include<iostream>
#include<string>
using namespace std;
string s,st;
int n,m,v,p,q,f[100000],zv[100000],zw[100000],fw[100000][5],fv[100000][5];
int main()
{
while(cin>>st>>s)//多組輸入
{
int i=0,j=0,s_end=s.size(),st_end=st.size();//設定指標
while(1)//一直尋找,找到為止
{
if(st[i]==s[j])//匹配2個字母,如果匹配成功,則尋找下一個字母
{
i++;//移動
j++;
if(i==st_end)//如果全部匹配成功,退出
{
cout<<"Yes"<<endl;
break;
}
}
else
{
j++;//如果2個字母無法成功匹配,那就在長串選下一個字母再找一次
if(j==s_end)//如果全部匹配失敗,退出
{
cout<<"No"<<endl;
break;
}
}
}
}
return 0;
}
瘋狂的採藥 經典完全揹包
題目背景
此題為紀念 \(\rm LiYuxiang\) 而生。
題目描述
\(\rm LiYuxiang\) 是個天資聰穎的孩子,他的夢想是成為世界上最偉大的醫師。為此,他想拜附近最有威望的醫師為師。醫師為了判斷他的資質,給他出了一個難題。醫師把他帶到一個到處都是草藥的山洞裡對他說:“孩子,這個山洞裡有一些不同種類的草藥,採每一種都需要一些時間,每一種也有它自身的價值。我會給你一段時間,在這段時間裡,你可以採到一些草藥。如果你是一個聰明的孩子,你應該可以讓採到的草藥的總價值最大。”
如果你是\(\rm LiYuxiang\),你能完成這個任務嗎?
此題和原題的不同點:
- 每種草藥可以無限制地瘋狂採摘。
- 藥的種類眼花繚亂,採藥時間好長好長啊!師傅等得菊花都謝了!
輸入格式
輸入第一行有兩個整數,分別代表總共能夠用來採藥的時間 \(\rm t\) 和代表山洞裡的草藥的數目 \(\rm m\)。
第2到第 \(\rm (m+1)\) 行,每行兩個整數,第\(\rm (i+1)\) 行的整數 \(\rm a_i\), \(\rm b_i\)分別表示採摘第 \(\rm i\) 種草藥的時間和該草藥的價值。
輸出格式
輸出一行,這一行只包含一個整數,表示在規定的時間內,可以採到的草藥的最大總價值。
輸入輸出樣例
輸入 #1
\(70\) \(3\)
\(71\) \(100\)
\(69\) \(1\)
\(1\) \(2\)
輸出 #1
\(140\)
說明/提示
資料規模與約定
-
對於 30% 的資料,保證\(m≤10^3\) 。
-
對於 100% 的資料,保證\(1≤m≤10^4\),\(1≤t≤10\),且 \(m×t\) < \(10^7\),\(1 ≤a_i\), \(b_i≤10^4≤a\)。
Code
#include<bits/stdc++.h>
using namespace std;
long long m,f[10000000],W,w[10000],v[10000];
int main()
{
cin>>W>>m;
for(int i=1;i<=m;i++)
cin>>w[i]>>v[i];//普普通通的輸入
for(int i=1;i<=m;i++)
for(int j=w[i];j<=W;j++)//完全揹包基本模板
f[j]=max(f[j-w[i]]+v[i],f[j]);
cout<<f[W];
return 0;
}