1.接苹果
【问题描述】
奶牛喜欢吃苹果。约翰有两棵苹果树,有N只苹果会从树上陆续落下。如果掉苹果的时候,贝西在那棵树下,她就能接住苹果。贝西一开始在第一棵树下。在苹果掉落之前,她有足够的时间来回走动,但她很懒,最多只愿意移动K次。请计算一下她最多可以接住几只苹果。
【输入】
第一行:两个整数 \(N\) 和 \(K\),\(1≤N≤1000;1≤K≤30\)
第 \(i+1\) 行有一个整数 \(T_i\),表示第i只苹果从哪棵树上掉落,\(1\)表示从第一棵树,\(2\)表示从第二棵树
【输出】
单个整数:表示能接住的最大苹果数量
【输入样例】
7 2
2
1
1
2
2
1
1
【输出样例】
6
【解释】
先待在第一棵树下接住两个,然后移动到第二棵树下接住两个,再返回第一棵树接住最后两个
这是一个显然的裸dp题,f[i][j],表示第i时间,已经移动j次取得的最大值,状态转移方程为:
然而我用了 \(f(i,j,k)\) 表示表示第 \(i\) 时间,已经移动 \(j\) 次,并且在第 \(k\) 树下时的
代码如下(非正解):
#include<bits/stdc++.h>
using namespace std;
int n,k,t[1001],a[1001],f[1001][31][3],x,num=0;
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&t[i]);
for(int j=0;j<=k;j++)
for(int i=1;i<=n;i++){
if(t[i]==1){
f[i][j][1]=max(f[i-1][j-1][2]+1,f[i-1][j][1]+1);
f[i][j][2]=f[i-1][j][2];
}
else{
f[i][j][2]=max(f[i-1][j-1][1]+1,f[i-1][j][2]+1);
f[i][j][1]=f[i-1][j][1];
}
}
printf("%d\n",max(f[n][k][1],f[n][k][2]));
return 0;
}
很明显可以看出来
for(int j=0;j<=k;j++)
for(int i=1;i<=n;i++){
if(t[i]==1){
f[i][j][1]=max(f[i-1][j-1][2]+1,f[i-1][j][1]+1);
f[i][j][2]=f[i-1][j][2];
}
else{
f[i][j][2]=max(f[i-1][j-1][1]+1,f[i-1][j][2]+1);
f[i][j][1]=f[i-1][j][1];
}
}
代码中j会变成负数,f数组会RE掉!但是,并没有。。。
这时,机房的rxj dalao说:“因为数组的存储原因 \(f[i][-1][k]\) ,对应的是t数组的后几位,而后几位对应的是0,对答案不会产生影响。”所以,就过了。如果要正确的代码,可以加一个特判。
ps.考试时因为想到RE的可能性,所以在考试的时候用了一个神奇的+1,然后就炸了(70分)
ps.ps.rxj
2.奶牛飞盘队
【问题描述】
农夫顿因开始玩飞盘之后,约翰也打算让奶牛们享受飞盘的乐趣.他要组建一只奶牛飞盘队.
他的N(1≤N≤2000)只奶牛,每只部有一个飞盘水准指数Ri(1≤Ri≤100000).约翰要选出1只或多于1只奶牛来参加他的飞盘队.
约翰比较迷信,他的幸运数字是F,所以他要求队伍的总能力必须是F的倍数。请帮他算
下,符合这个要求的队伍组合有多少?由于这个数字很大,只要输出答案除以10^8 的余数就可以了。
【输入】
第一行:两个用空格分开的整数:N和F,1 ≤ N ≤ 2000,1 ≤ F ≤ 1000
第二行到N + 1行:第i + 1行有一个整数Ri ,表示第i头奶牛的能力,1 ≤ Ri ≤ 10^5
【输出】
单个整数,表示方案数%\(10^8\)
【输入样例】
4 5
1
2
8
2
【输出样例】
3
FJ has four cows whose ratings are 1, 2, 8, and 2. He will only accept a team whose rating sum is a multiple of 5.
FJ can pair the 8 and either of the 2's (8 + 2 = 10), or he can use both 2's and the 1 (2 + 2 + 1 = 5).
第i只牛有两种可能性,要还是不要,所以,这个我们可以往0/1背包上考虑。
因为我们要求的是F的整数倍的方案数,我们只考虑这种可能性显然无法满足最优子结构,按照我们常见的套路,我们显然要把当前组成的累加和对 F mod 的值进行存储。
于是乎就有
代码如下
#include<bits/stdc++.h>
using namespace std;
int n,F,r[2001];
long long MOD=100000000,f[2001][2001],ans;
int main(){
scanf("%d%d",&n,&F);
for(int i=1;i<=n;i++)
scanf("%d",&r[i]);
for(int i=1;i<=n;i++){
int t=r[i]%F;
if(!t) t=F;
f[i][t]=1;
if(i!=1){
for(int j=1;j<=F;j++){
t=(j+r[i])%F;
if(!t) t=F;
f[i][t]=(f[i][t]+f[i-1][j])%MOD;
f[i][j]=(f[i][j]+f[i-1][j])%MOD;
}
}
}
printf("%lld",f[n][F]);
return 0;
}
ps.考试的时候看什么都是区间dp,然后。。。样例过了,而且只有样例过了(10分)
3.股票市场
【问题描述】
【输入样例】
2 3 10
10 15 15
13 11 20
【输出样例】
24
在第3天购买某种股票然后在第5天卖,可以看成第3天购买,第4天卖出,第4天再购买,第5天再卖出。
我们只关心低价购买,高价卖出,而股票的获利于你持股的时间没有关系,每天的买卖也没有限制,因此我们可以依次考虑每相邻的两天我们如何购买前一天的股票然后在后一天全部卖出。
这样我们得到一个贪心算法,依次考虑每相邻两天,前一天买入后一天全部卖出所能得到的最大收益,而这正是个简单的背包问题:股票就是物品,花费就是前一天的价格,收益就是后一天的价格,背包就是资金。
代码如下
#include<bits/stdc++.h>
using namespace std;
int s,d,m,a[101][101],f[1000010];
int main(){
scanf("%d%d%d",&s,&d,&m);
for(int i=1;i<=s;i++)
for(int j=1;j<=d;j++)
scanf("%d",&a[j][i]);
for(int k=1;k<d;k++){
memset(f,0,sizeof(f));
for(int i=1;i<=s;i++)
for(int j=a[k][i];j<=m;j++)
f[j]=max(f[j],f[j-a[k][i]]+a[k+1][i]-a[k][i]);
m+=f[m];
}
printf("%d",m);
return 0;
}
ps.考试时没有时间考虑,先写4,5了,最后只输出了样例(7分)
4.248
【问题描述】
给定一个1*N(2<=N<=248)的地图,在里面玩2048,每次可以合并相邻两个(数值范围1-40),问最大能合出多少。注意合并后的数值并非加倍而是+1,例如2与2合并后的数值为3。
【输入】
第一行,一个整数N
接下来N行,每行一个整数,表示第i个数字
【输出】
所求的答案
【输入样例】
4
1
1
1
2
【输出样例】
3
先合并第2个和第3个1,得到1 2 2 。再合并两个2,得到3。
很别扭的一道题,显然是区间之间要合并得到最大的值,但是合并之前有个条件,这两个要合并的区间的值要一样。
代码如下
#include<bits/stdc++.h>
using namespace std;
int n,a[301],f[301][301],ans=-1,maxx=-1;
int main(){
freopen("248.in","r",stdin);
freopen("248.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
maxx=max(maxx,a[i]);
}
for(int i=1;i<=n;i++) f[i][i]=a[i];
for(int i=1;i<n;i++)
for(int j=1;j<=n;j++){
int l=i+j;
if(l>n)break;
for(int r=j;r<l;r++)
if(f[j][r]==f[r+1][l]){
f[j][l]=max(f[j][l],f[j][r]+1);
ans=max(ans,f[j][l]);
}
}
printf("%d\n",max(ans,maxx));
return 0;
}