A
模拟操作题
#include
using namespace std;
#define ll long long
#define gc getchar
inline int read(){
int f=1,res=0;
char ch=gc();
while(!isdigit(ch)) f^ch=='-',ch=gc();
while(isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=gc();
return f?res:-res;
}
const int N=1010;
int f[N];
int path[N][N];
int a[N];
int n,m,k;
int v[N],w[N];
int main(){
n=read(),m=read(),k=read();
for(int i=1;i<=n;i++)
v[i]=read(),w[i]=read();
for(int i=1;i<=n;i++)
for(int j=m;j>=w[i];j--){
if(f[j]<f[j-w[i]]+v[i]) {
f[j]=f[j-w[i]]+v[i];
path[i][j]=1;
}
}
int cnt=0;
int p=n,q=m;
while(p>=1&&q>=0){
if(path[p][q]){
a[++cnt]=v[p];
q-=w[p];
}
p--;
}
sort(a+1,a+cnt+1);
int sum=0;
if(cnt<n){
for(int i=cnt;i;i--){
if(k>0) {
k--;
sum+=a[i];
}
sum+=a[i];
}
printf("%d\n",sum);
}
else{
for(int i=cnt;i>1;i--){
if(k>0) {
k--;
sum+=a[i];
}
sum+=a[i];
}
printf("%d\n",sum);
}
return 0;
}
B
简单的背包题,对父母要加特殊判断,如果背包能装下全部东西,去掉价值最小的。
如果没能装下全部东西,则把背包的价值计算起来就可以了。
#include
using namespace std;
#define ll long long
#define gc getchar
inline int read(){
int f=1,res=0;
char ch=gc();
while(!isdigit(ch)) f^ch=='-',ch=gc();
while(isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=gc();
return f?res:-res;
}
const int N=1010;
int f[N];
int path[N][N];
int a[N];
int n,m,k;
int v[N],w[N];
int main(){
n=read(),m=read(),k=read();
for(int i=1;i<=n;i++)
v[i]=read(),w[i]=read();
for(int i=1;i<=n;i++)
for(int j=m;j>=w[i];j--){
if(f[j]<f[j-w[i]]+v[i]) {
f[j]=f[j-w[i]]+v[i];
path[i][j]=1;
}
}
int cnt=0;
int p=n,q=m;
while(p>=1&&q>=0){
if(path[p][q]){
a[++cnt]=v[p];
q-=w[p];
}
p--;
}
sort(a+1,a+cnt+1);
int sum=0;
if(cnt<n){
for(int i=cnt;i;i--){
if(k>0) {
k--;
sum+=a[i];
}
sum+=a[i];
}
printf("%d\n",sum);
}
else{
for(int i=cnt;i>1;i--){
if(k>0) {
k--;
sum+=a[i];
}
sum+=a[i];
}
printf("%d\n",sum);
}
return 0;
}
C
拿了个暴力20分!
dp做法:
首先将美味值排序
设 d p i , j dp_{i,j} dpi,j以第i种结尾选了j的种的方案数
可以发现 d p i , j dp_{i,j} dpi,j为 ∑ a p ∗ l < = a i a p ∗ r > = a i d p p . j − 1 \sum_{a_p*l<=a_i}^{ap*r>=a_i}dp_{p.j-1} ∑ap∗l<=aiap∗r>=aidpp.j−1
这个过程三重for 循环模拟可解为 ∑ i = k n d p i , k \sum_{i=k}^ndp_{i,k} ∑i=kndpi,k
然而只能得到40分
我们可以设 d p i , j dp_{i,j} dpi,j为到第i中选了j种的方案数
首先很显然 d p i − 1 , j → d p i , j dp_{i-1,j}\to dp_{i,j} dpi−1,j→dpi,j
然后我们只需知道对于每个 i 最大的 p 和最小的 p,满足 a p ∗ l < = a i , a p ∗ r > = a i a_p*l<=a_i,a_p*r>=a_i ap∗l<=ai,ap∗r>=ai
这个过程我们可以O(n)预处理得到
int mi[N],ma[N];
int li=0,la=0;
for(int i=1;i<=n;i++){
while(va[la+1]*l<=va[i]&&la<i-1) la++;
while(va[li+1]*r<va[i]&&li<i-1) li++;
mi[i]=li;
ma[i]=la;
dp[i][1]=i;//初始化
}
其中 m a i ma_i mai表示最大的p使得 a p ∗ l < = a i a_p*l<=a_i ap∗l<=ai而 m i i mi_i mii表示最大的q使得 a q ∗ r < a i a_q*r
那么 ∑ a p l < = a i a p ∗ r > = a i d p p , j − 1 \sum_{a_pl<=a_i}^{a_p*r>=_ai}dp_{p,j-1} ∑apl<=aiap∗r>=aidpp,j−1
就是 d p m a i , j − 1 − d p m i i , j − 1 dp_{ma_i,j-1}-dp_{mi_i,j-1} dpmai,j−1−dpmii,j−1
综上转移状态方程就是
dp[i][j] = (dp[i-1][j] + dp[ma[i]][j-1] - dp[mi[i]][j-1] + mod) % mod;
由于 m o d × 2147483647 mod\times2147483647 mod×2147483647 , 所有变量只需要用 int,边算边模,然后开滚动数组即可。
注意:
转移中每次要第从 1 开始遍历到 n ,以重置过期状态,否则会受到以前状态的影响。
对于问题2
还是 dp, 转移方程也仅有一行。
设 f i , j 为 到 第 i 中 选 了 j 种 的 最 大 美 味 值 的 和 f_{i,j}为到第i中选了j种的最大美味值的和 fi,j为到第i中选了j种的最大美味值的和
很显然还是 f i − 1 , j → f i , j f_{i-1,j}\to f_{i,j} fi−1,j→fi,j
然后发现 f m a i , j − 1 + a i → f i , j f_{ma_i,j-1}+a_i\to f_{i,j} fmai,j−1+ai→fi,j两者取最大值即可。
但仅当以第 i种结尾选 k 种的方案存在时,我们才能考虑后者。
所以转移方程为:
f[i][j&1]=max(f[i-1][j&1],(dp[i][j&1]!=dp[i-1][j&1]?f[ma[i]][j&1^1]+va[i]:0))%mod;
综上
#include
using namespace std;
#define ll long long
#define gc getchar
#define pb push_back
inline int read(){
int f=1,res=0;
char ch=gc();
while(!isdigit(ch)) f^ch=='-',ch=gc();
while(isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=gc();
return f?res:-res;
}
const int N=2000005;
const int mod=1e9+7;
int n,k;
int l,r;
int va[N];
int mi[N],ma[N];
int dp[N][2];
int f[N][2];
int la,li;
int main(){
n=read(),k=read(),l=read(),r=read();
for(int i=1;i<=n;i++)va[i]=read();
sort(va+1, va+1+n);
for(int i=1;i<=n;i++) {
while(va[la+1] * l <= va[i] && la+1 < i)la++;
while(va[li+1] * r < va[i] && li+1< i)li++;
mi[i]=li;
ma[i]=la;
dp[i][1]=i;
f[i][1]=va[i];
}
for(int j=2;j<=k;j++) {
for(int i=1;i<=n;i++) {
if(dp[ma[i]][j&1^1] < dp[mi[i]][j&1^1])
dp[i][j&1] = (dp[i-1][j&1] + dp[ma[i]][j&1^1] - dp[mi[i]][j&1^1] + mod ) % mod;
else
dp[i][j&1] = (dp[i-1][j&1] + dp[ma[i]][j&1^1] - dp[mi[i]][j&1^1] ) % mod;
f[i][j&1] = max(f[i-1][j&1], (dp[i][j&1] != dp[i-1][j&1] ? f[ma[i]][j&1^1] + va[i] : 0 ) ) % mod;
}
}
cout<<dp[n][k&1]<<endl<<f[n][k&1];
}