dp练习总结

dp练习赛


T1

免费馅饼

有一天, 小王同学正走在路上,忽然天上掉下大把大把的馅饼(哈哈哈。。。。)。这个只能说小王同学的人品太好,这馅饼就掉落在他身旁的10米范围内。所以小王同学马上去接馅饼,因为掉在地方的馅饼就不能吃了。他只能在这个10米范围内接馅饼。由于小王同学是一个非常优秀的Oier,但他不是一个优秀的运动员,所以他每秒钟只有在移动不超过一米的范围内接住坠落的馅饼。现在给这条小径如图标上坐标:

为了使问题简化,假设在接下来的一段时间里,馅饼都掉落在0-10这11个位置。开始时小王站在5这个位置上,因此在第一秒,他只能接到4,5,6这三个位置中其中一个位置上的馅饼。问小王最多可能接到多少个馅饼?(假设他的背包可以容纳无穷多个馅饼)
Input
输入数据有多组。每组数据的第一行为以正整数n(0

解析

这道题我看到的第一印象就是FJ接苹果,但一看数据十万就知道一定会T飞,所以必须得换一种思路。根据题意,小王一个单位时间里最多只能走一格,所以我们可以从小到大枚举时间,而当前时间的下一秒的位置,肯定由当前时间往左走、往右走或不动得到。所以就可以递推。

代码如下

#include
using namespace std;
const int dx[3]={0,1,-1};//不动,往右,往左
const int MAXN=100050;
int n,maxt,dp[11][MAXN],b[11][MAXN],maxn=-MAXN;
inline int read(){
    int num=0,f=1;
    char c=getchar();
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')f=-1;
    for(;c>='0'&&c<='9';c=getchar())
    num=(num<<1)+(num<<3)+c-48;
    return num*f;
}
void work(){
    maxn=0;
    dp[5][0]=0;
    b[5][0]=0;//多组数据别忘了清空数组
    for(int i=0;ifor(int p=0;p<=10;++p)
        if(dp[p][i]>=0){
            for(int k=0;k<=2;++k){
                int newp=p+dx[k];
                if(newp<0||newp>10)continue;//边界
                if(b[newp][i+1]<=0)dp[newp][i+1]=max(dp[newp][i+1],dp[p][i]);//当时没有饼掉落
                else dp[newp][i+1]=max(dp[p][i]+b[newp][i+1],dp[newp][i+1]);//当时有饼掉落
                maxn=max(dp[newp][i+1],maxn);
            }
        }
    }
    printf("%d\n",maxn);

}
void init(){
    while(1){
        n=read();
        if(n==0)exit(0);
        memset(b,-1,sizeof(b));
        memset(dp,-10,sizeof(dp));
        for(int i=1;i<=n;++i){
            int p=read();
            int ti=read();
            maxt=max(maxt,ti);
            if(b[p][ti]<0)b[p][ti]=0;
            b[p][ti]++;
        }
        work();
    }
}
int main(){
    init();
    return 0;
}

T2

蛋糕

一个蛋糕,可以一次吃一口或一次吃k口,那么问体积为x1~x2不定的蛋糕有多少种吃法。

Input

第一行有两个正整数t,k(1<=t,k<=100000) ,其中t表示数据的组数。
接下来t行,每行两个数x1, x2(1<=x1<=x2<=100000)。
Output
共t行,每行一个正整数x,表示蛋糕数量在x1-x2之间时,一共能有几种不同的吃法,结果对(10^9+7)取模

Sample Input

3 2
1 3
2 3
4 4

Sample Output

6

5

5

解析

这道题很水,只要爬过楼梯就一定会做。需要注意的一点是当k=1时,一次吃一口和一次吃k口是不同的!!!
另外因为有多组数据,为了防止超时,递推出最大的x2的方法数,然后求前缀和,查询区间和时只要sum[x2]-sum[x1-1]就好了。

代码

#include
using namespace std;
const int MAXN=1000000007;
int t,k,x1,x2,dp[100050],ans,maxn,sum[100050];
struct node{
    int l,r;
}e[100050];
inline int read(){
    int num=0,f=1;
    char c=getchar();
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')f=-1;
    for(;c>='0'&&c<='9';c=getchar())
    num=(num<<1)+(num<<3)+c-48;
    return num*f;
}
void init(){
    t=read();k=read();
    for(int i=1;i<=t;++i){
        e[i].l=read();
        e[i].r=read();
        maxn=max(maxn,e[i].r);
    }
}
void work(){
    dp[0]=1;
    for(int i=1;i<=maxn;++i){
        dp[i]=dp[i-1];
        if(i-k>=0)dp[i]+=dp[i-k];
        dp[i]%=MAXN;
        sum[i]=(sum[i-1]+dp[i])%MAXN;
    }
    for(int i=1;i<=t;++i){
        if(sum[e[i].r]<sum[e[i].l-1])printf("%d\n",sum[e[i].r]+MAXN-sum[e[i].l-1]);//%的意义下做减法要注意先在被减数上加上MAXN防止出现负数
        else printf("%d\n",sum[e[i].r]-sum[e[i].l-1]);
    }
}
int main(){
    init();
    work();
    return 0;
}

T3

水果

有n个水果, 每个水果都有两个属性值ai表示美味程度, bi表示能量值, 现在要求选出一个或多个水果, 使得选出的水果的ai和与bi和的比例是k 问在这种清形可能出现的情况下ai的和最多是多少, 如果这样的情形不存在输出 -1

Input

第一行包含两个整数n, k (1 ≤ n ≤ 100, 1 ≤ k ≤ 10). 第二行为 n个整数 a1, a2, …, an (1 ≤ ai ≤ 100) — 水果的美味值. 第三行包含水果的能量值。

Output

如果无解输出-1.
否则输出满足条件的ai累加和

Examples

Input

3 2
10 8 1
2 7 1

Output

18

Input

5 3
4 4 4 4 4
2 2 2 2 2

Output

-1

解析

首先这道题不能用搜索,n=100肯定会超时。看到题目中要求让美味值的和最大,所以就想到了背包。但直接背包肯定又做不了,所以我们就转换一下。
根据题意:suma/sumb=k ,所以suma=sumb*k ,所以当背包容量为suma-sumb*k=0时,就是我们要求的最大值。又因为suma-sumb可能是负数,所以将suma-sumb整体偏移p,使得他们都为正,来做数组下标。

代码

#include
using namespace std;
const int p=100000;//偏移量
int n,k,dp[150][200000];
struct node{
    int a,b,c;//a为美味值,b为能量值,c为a-b*k;
}e[150];
inline int read(){
    int num=0,f=1;
    char c=getchar();
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')f=-1;
    for(;c>='0'&&c<='9';c=getchar())
    num=(num<<1)+(num<<3)+c-48;
    return num*f;
}
void init(){
    n=read();k=read();
    for(int i=1;i<=n;++i)e[i].a=read();
    for(int i=1;i<=n;++i){
        e[i].b=read();
        e[i].c=e[i].a-e[i].b*k;
    }
}
void work(){
    memset(dp,-10,sizeof(dp));
    dp[0][p]=0;
    for(int i=1;i<=n;++i)
        for(int j=p*2;j>=e[i].c;--j){
            dp[i][j]=max(dp[i-1][j-e[i].c]+e[i].a,dp[i-1][j]);
        }
        if(dp[n][p]==0)dp[n][p]=-1;
    printf("%d",dp[n][p]);
}
int main(){
    init();
    work();
    return 0;
}

你可能感兴趣的:(日志)