社区五一活动
目錄
開心的金明
榨取kkksc03
合唱隊形
導彈攔截
紀念品分組
開心的金明
解析:
????????這道題給出n(總錢數)和m(物品個數),每個物品給出價錢和重要程度,求不超過總錢數的物品價格與重要度乘積的總和,即v1*p1+v2*p2+……。這乍一看是不是很像01背包問題。
????????01背包問題問的是給出一個背包的物品個數和總體積,每個物品給出所占體積及價值,求在不超過背包總體積的價值之和。
? ? ? ? 設f[i][j]表示選前i個物品花費j個金幣的值。分析一下狀態轉移方程:這道題求的是最大值,所以是max,每個物品只有選或不選。不選:f[i][j]=f[i-1][j],選:f[i][j]=f[i-1][j-v[i]],所以狀態轉移方程就是f[i][j]=max(f[i-1][j], f[i-1][j-v[i]]*p[i]),答案就是f[n][m]
? ? ? ? 然后我們發現選前i個物品后的f是由選i-1物品所決定的,所以我們可以降為一維,則f[j]=max(f[j], f[j-v[i]]*p[i]),答案是f[m]
代碼:
#include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <stack> #include <queue> #define INF 0x3f3f3f3f using namespace std; typedef long long LL; LL dp[30][30000];//前i個花費價格為j的最大 int n,m; int v[30],p[30];int main() {cin>>n>>m;for(int i=1; i<=m; i++)cin>>v[i]>>p[i];for(int i=1; i<=m; i++)for(int j=1; j<=n; j++) {if(j<v[i]) dp[i][j]=dp[i-1][j];else dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+p[i]*v[i]);}cout<<dp[m][n];return 0; } #include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <stack> #include <queue> #define INF 0x3f3f3f3f using namespace std; typedef long long LL; LL dp[30000];//前i個花費價格為j的最大 int n,m; int v[30],p[30]; //快讀,可以不用管 int read() {char c=getchar();int date=0,w=1;while(c<'0' || c>'9') {if(c=='-') w=-1;c=getchar();}while(c>='0' && c<='9') {date=date*10+(c-'0');c=getchar();}return date*w; }int main() {n=read(),m=read();for(int i=1; i<=m; i++)v[i]=read(),p[i]=read();for(int i=1; i<=m; i++)for(int j=n; j>=v[i]; j--)dp[j]=max(dp[j],dp[j-v[i]]+v[i]*p[i]);cout<<dp[n];return 0; }榨取kkksc03
?解析:
? ? ? ?這道題與上面的題類似,不過這是一個二維費用流,上一題是體積限制,這題是金幣和時間限制,所以這題是三維,可以壓縮成二維。設f[i][j][k]表示選前i個物品金幣為j時間為k時實現的愿望個數,不選的話:f[i-1][j][k],選的話:f[i-1][j-v[i]][k-w[i]],狀態轉移方程:f[i][j][k]=max(f[i-1][j][k],f[i-1][j-v[i]][k-w[i]]);壓縮成二維就是去掉i那一維。
#include <bits/stdc++.h> #define MOD 1000000007 #define INF 0x7f7f7f7f using namespace std; typedef long long LL; int n,m,t; int f[205][205],v[105],w[105]; int main() {cin>>n>>m>>t;for(int i=1; i<=n; i++)cin>>v[i]>>w[i];for(int i=1; i<=n; i++)for(int j=m; j>=v[i]; j--)for(int k=t; k>=w[i]; k--)f[j][k]=max(f[j][k],f[j-v[i]][k-w[i]]+1);cout<<f[m][t];return 0; }?
合唱隊形
解析:
? ? ? ? 題中給出了一堆數構成的無序數列,問我們至少去掉其中n-k個使得該數列符合給出的式子,我們暫且就認為該數列是一個類拋物線,且兩個元素之間不能相等。下面的該數列指的是取出k個元素后的數列,ti是峰值且1 ≤ i ≤ k,所以ti這個峰值可以在最左邊或者在最右邊。當它在最左邊的時候,該數列是嚴格單調遞減數列;當它在最右邊時,該數列是嚴格單調遞增數列;當它在中間某個位置時,該數列先嚴格單調遞增再嚴格單調遞減。
? ? ? ? 我們用f[]和g[]來存儲符合嚴格單增和嚴格單減的最多元素個數,用樣例為例186 186 150 200 160 130 197 220,我們至少去掉4個元素才能使得它和題目的式子一樣,哪4個?186 150 197 220.
? ? ? ? 我們使用f[]來存儲符合嚴格單增的最多元素個數,f[1]當然等于1,做完for后,可以得到f[2] = 1;f[3] = 1;f[4] = 2 = f[1]+1 = f[2]+1 = f[3]+1:形成的嚴格單增數列有186 200,186 200,150 200 ,f[5] = 1;f[6] = 1;f[7] = 2 =? f[1]+1 = f[2]+1 = f[3]+1 = f[5]+1 = f[6]+1:186 197,186 197,150 197,160 197,130 197;f[8] = 3 = f[4]+1 = f[7] + 1:186 200 220,186 197 220……從這里可以的出結論:設前面的元素是i,后面的元素是j,當a[i]<a[j]時,判定f[i]+1>=f[j](j這個元素可以被納入數列中,所以需要判定時+1),如果成立則做,否則繼續遍歷。
? ? ? ? 我們用g[]來存儲符合嚴格單減的最多元素個數,與上面同理推導,不過這里有個陷阱。也許很多人會說:上面是單增且后面所表示元素個數<=前面所表示元素個數+1(即f[i]+1>=f[j]),這里是單減且后面所表示元素個數<=前面所表示元素個數+1(即g[i]+1>=g[j]),那么是不是把上面的兩重for挪到下面將小于改大于即可,理論上是可行的,這樣的確可以得出前i個元素中符合嚴格單減的最大元素個數,可是這樣做沒有意義,為什么呢?
? ? ? ? 回到題目中去,它讓我們求最少幾位同學出列使得數列符合題目中的式子,我們求出當前數列前面嚴格單增的最多元素個數及后面嚴格單減的最多元素個數,相加后就可以得到符合題目中的式子的數列的最多元素個數k,n-k即是題目答案。假如單減這里順著來,g[]表示的是前面的數從左往右嚴格單減,而題目需要的是后面的數從左往右嚴格單減(即從右往左嚴格單減),所以我們需要逆著來計算。
? ? ? ? 由于g[]是逆著來算,即g[i]是從第i個往后符合嚴格單調遞減的最多元素個數,那么f[i]+g[i]就表示第1個到第i個符合嚴格單調遞增的最多元素個數+第i個到第n個符合嚴格單調遞減的最多元素個數,即題目中給到的式子,不過需要-1,因為第i個元素被算了兩遍,所以f[i]+g[i]-1就是第i個元素為峰值的符合式子的元素個數。求n-k的最小值,只要保證k是最大值即可。
#include <bits/stdc++.h> #define MOD 1000000007 #define INF 0x7f7f7f7f using namespace std; typedef long long LL; int n,maxn; int a[105],f[105],g[105]; int main() {cin>>n;for(int i=1; i<=n; i++) {cin>>a[i];f[i]=g[i]=1;}//從左往右單增(i右j左,a[i]為較大的)for(int i=2; i<=n; i++)for(int j=1; j<i; j++)if(a[j]<a[i] && f[i]<=f[j]+1)f[i]=f[j]+1;//從左往右單減(i左j右,a[i]為較大的)(從右往左單增)for(int i=n-1; i>=1; i--)for(int j=n; j>i; j--)if(a[i]>a[j] && g[i]<=g[j]+1)g[i]=g[j]+1;for(int i=1; i<=n; i++) maxn=max(maxn,f[i]+g[i]-1);cout<<n-maxn;return 0; }導彈攔截
?解析:
????????這道題有兩個問,第一個問的知識點是最長不上升子序列,第二個問問的是最長上升子序列。我們可以直接用dp來做,不過這個復雜度是n^2,只能獲得100分(我想應該不用給出dp的做法了)。要想獲得200分,復雜度需要降為nlogn,那么我們就需要用到二分+貪心。(下面的分析是之前的博客里面的)具體證明過程
???????第一問:最長不上升子序列。如果小于等于low中最小的元素就進low,如果大于就找low中第一個小于等于n(需要進low的元素)的位置。由于這個是最長不上升子序列,所以從左到右是從大到小(左邊最大,右邊最小),第一個小于等于n應該是往小的找(右邊),所以我們判定條件時盡量將n放在右邊,也就是說low[mid] >= n,mid是中間的位置,n比小于等于的話就在右邊,在右邊就應該往右邊找,也就是l = mid + 1。
? ? ? ? 第二問:最長上升子序列。如果大于low中最大的元素就進low,如果小于等于的話就找low中第一個大于n(需要進low的元素)的位置。由于這是最長上升子序列(嚴格單增),所以左小右大,我們需要找第一個大于n的值,應該往大的找(右邊),所以我們判定條件時就應該將n置于右邊,也就是low[mid] < n,mid是中間的位置,n比它大的話就在右邊,在右邊也就往右邊找,即l = mid+1.
#include <bits/stdc++.h> #define INF 0x3f3f3f3f #define MOD 1000000009 using namespace std; typedef long long LL; int n,low[100010],a[100010],b[100010]; int ans,cnt;int main() {int s=0;memset(low,0,sizeof(low));while(scanf("%d",&n)!=EOF && n) a[++s]=n;ans=cnt=1;low[1]=a[1];for(int i=2; i<=s; i++) {if(a[i]<=low[ans]) low[++ans]=a[i];else {int l=1,r=ans;while(l<r) {int mid=(l+r)/2;if(low[mid]>=a[i]) l=mid+1;else r=mid;}low[r]=a[i];}}memset(low,0,sizeof(low));low[1]=a[1];for(int i=2; i<=s; i++) {if(a[i]>low[cnt]) low[++cnt]=a[i];else {int l=1,r=cnt;while(l<r) {int mid=(l+r)/2;if(low[mid]<a[i]) l=mid+1;else r=mid;}low[r]=a[i];}}cout<<ans<<endl<<cnt;return 0; }紀念品分組
?解析:
????????這題的考點是貪心+雙指針。首先題目問的是最少分組數目,可以得到用貪心算法;其次,題目中說道一個禮盒中最多放兩件物品,所以我們應該盡量在每個禮盒中放兩個物品且不超過總價格的上上限,這里就是需要用到雙指針。
? ? ? ? 這道題如何貪呢?放到同一個盒子里面的兩個物品應該盡量大和盡量小的,或者放當前最大價格的物品進去,那么我們需要進行排序,排完后就開始雙指針操作。用l指向第一個物品(價格最小的),用r指向最后一個物品(價格最大的),然后盡量將被指向的兩個物品放到同一個盒子里。題目中的輸入保證每個物品小于等于單個盒子價格的上上限。假如被指向的兩個物品在上上限之內,那么就將該兩個物品放進去,雙指針往里面夾;假如被指向的兩個物品大于上上限,那么我們就將最大價格的物品放進盒子里,r就需要更新。
#include <bits/stdc++.h> #define MOD 1000000007 #define INF 0x3f3f3f3f using namespace std; typedef long long LL; int n,w,a[100005]; LL s; int main() {cin>>w>>n;for(int i=1; i<=n; i++)cin>>a[i];sort(a+1,a+1+n);int l=1,r=n;while(l<=r) {if(l!=r && a[l]+a[r]<=w) {s++;l++;r--;}else {s++;r--;}}cout<<s;return 0; }? ? ? ? 本人大一,功底較差,如有錯誤請指正,必將耐心聽取。
總結