腾讯马拉松 初赛 DP集

小明系列故事——买年货

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 998    Accepted Submission(s): 411


Problem Description
  春节将至,小明要去超市购置年货,于是小明去了自己经常去的都尚超市。
  刚到超市,小明就发现超市门口聚集一堆人。用白云女士的话说就是:“那家伙,那场面,真是人山人海,锣鼓喧天,鞭炮齐呤,红旗招展。那可真是相当的壮观啊!”。好奇的小明走过去,奋力挤过人群,发现超市门口贴了一张通知,内容如下:
  
   值此新春佳节来临之际,为了回馈广大顾客的支持和厚爱,特举行春节大酬宾、优惠大放送活动。凡是都尚会员都可用会员积分兑换商品,凡是都尚会员都可免费拿k件商品,凡是购物顾客均有好礼相送。满100元送bla bla bla bla,满200元送bla bla bla bla bla...blablabla....
  
  还没看完通知,小明就高兴的要死,因为他就是都尚的会员啊。迫不及待的小明在超市逛了一圈发现超市里有n件他想要的商品。小明顺便对这n件商品打了分,表示商品的实际价值。小明发现身上带了v1的人民币,会员卡里面有v2的积分。他想知道他最多能买多大价值的商品。
  由于小明想要的商品实在太多了,他算了半天头都疼了也没算出来,所以请你这位聪明的程序员来帮帮他吧。
 

Input
输入包含多组测试用例。
每组数据的第一行是四个整数n,v1,v2,k;
然后是n行,每行三个整数a,b,val,分别表示每个商品的价钱,兑换所需积分,实际价值。
[Technical Specification]
1 <= n <= 100
0 <= v1, v2 <= 100
0 <= k <= 5
0 <= a, b, val <= 100

Ps. 只要钱或者积分满足购买一件商品的要求,那么就可以买下这件商品。
 

Output
对于每组数据,输出能买的最大价值。
详细信息见Sample。
 

Sample Input
  
  
5 1 6 1 4 3 3 0 3 2 2 3 3 3 3 2 1 0 2 4 2 5 0 0 1 0 4 4 1 3 3 4 3 4 4
 

Sample Output
  
  
12 4
 
                              

       典型的01背包的转化=》多维背包;
      将拥有的积分、钱数、以及可以免费拿的物品的件数作为三种限制条件,直接四重for循环解决。。。


代码:
   
#include<iostream>
using namespace std;
struct node{
       int v1;
       int v2;
       int w;
};
node a[105];
int dp[105][105][105];
int maxn(int a1,int a2){
    if(a1<a2)
        return a2;
    return a1;
}
int main(){
    int n,m1,m2,free;
    while(cin>>n>>m1>>m2>>free){
         for(int i=0;i<n;i++){
                 cin>>a[i].v1>>a[i].v2>>a[i].w;
                 }
         memset(dp,0,sizeof(dp));
         for(int i=0;i<n;i++){
                 for(int j=m1;j>=0;j--){
                         for(int k=m2;k>=0;k--){
                                 for(int g=free;g>=0;g--){
                                         int temp=0;
                                         if(j>=a[i].v1)
                                             temp=maxn(temp, dp[j-a[i].v1][k][g]+a[i].w);
                                         if(k>=a[i].v2)
                                             temp=maxn(temp, dp[j][k-a[i].v2][g]+a[i].w);
                                         if(g>=1)
                                            temp=maxn(temp, dp[j][k][g-1]+a[i].w);
                                         dp[j][k][g]=maxn(temp,dp[j][k][g]);
                                   //      dp[j][k][g]=max(dp[j-a[i].v1][k][g]+a[i].w,dp[j][k-a[i].v2][g]+a[i].w,dp[j][k][g-1]+a[i].w,dp[j][k][g]);
                                         }
                                         }
                                         }
                                         }
       //  for(int i=0;i<=free;i++)
         //      cout<<dp[m1][m2][i]<<endl;
         cout<<dp[m1][m2][free]<<endl;
         }
    return 0;
}







吉哥系列故事——临时工计划

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 1237    Accepted Submission(s): 462


Problem Description
  俗话说一分钱难倒英雄汉,高中几年下来,吉哥已经深深明白了这个道理,因此,新年开始存储一年的个人资金已经成了习惯,不过自从大学之后他不好意思再向大人要压岁钱了,只能把唯一的希望放到自己身上。可是由于时间段的特殊性和自己能力的因素,只能找到些零零碎碎的工作,吉哥想知道怎么安排自己的假期才能获得最多的工资。
  已知吉哥一共有m天的假期,每天的编号从1到m,一共有n份可以做的工作,每份工作都知道起始时间s,终止时间e和对应的工资c,每份工作的起始和终止时间以天为单位(即天数编号),每份工作必须从起始时间做到终止时间才能得到总工资c,且不能存在时间重叠的工作。比如,第1天起始第2天结束的工作不能和第2天起始,第4天结束的工作一起被选定,因为第2天吉哥只能在一个地方工作。
  现在,吉哥想知道怎么安排才能在假期的m天内获得最大的工资数(第m+1天吉哥必须返回学校,m天以后起始或终止的工作是不能完成的)。
 

Input
第一行是数据的组数T;每组数据的第一行是2个正整数:假期时间m和可做的工作数n;接下来n行分别有3个正整数描述对应的n个工作的起始时间s,终止时间e,总工资c。

[Technical Specification]
1<=T<=1000
9<m<=100
0<n<=1000
s<=100, e<=100, s<=e
c<=10000
 

Output
对于每组数据,输出吉哥可获得的最高工资数。
 

Sample Input
   
   
1 10 5 1 5 100 3 10 10 5 10 100 1 4 2 6 12 266
 

Sample Output
   
   
102







   由于每个时间点可以得到的最大工资,取决于这段工作的开始时间-1的时间点的最大工资,所以可以依次建立关系式;

   dp[j]=max(dp[start[i]-1]+value[i],dp[i]);

   由于每个时间点的最大工资确定后,会影响以后时间的工资,所以要使用两重for循环,对每一个时间点都从1~n计算不同工作对这一时间点的影响;

ps. 工作的时间段需要对结束时间进行排序;

代码:

#include<iostream>
using namespace std;
struct node{
       int start;
       int end;
       int value;
};
node a[1005];
int dp[105];
bool cmp(const node & a1,const  node & a2){
     return a1.end<a2.end;
}
int maxn(int b1,int b2){
    if(b1<b2)
       return b2;
    return b1;
}
int main(){
    int m;
    cin>>m;
    while(m--){
       int n,times;
       memset(dp,0,sizeof(dp));
     //  scanf("%d%d",×,&n);
       cin>>times>>n;
       for(int i=0;i<n;i++){
               scanf("%d%d%d",&a[i].start,&a[i].end,&a[i].value);
           //    cin>>a[i].start>>a[i].end>>a[i].value;
               }
       sort(a,a+n,cmp);
       for(int i=0;i<=times;i++){
               for(int j=0;j<n;j++){
                       if(a[j].end>i)
                              break;
                       dp[i]=maxn(dp[i],dp[a[j].start-1]+a[j].value);
                       }
                       }
       cout<<dp[times]<<endl;
       }
    return 0;
}





威威猫系列故事——篮球梦

Time Limit: 300/100 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 1524    Accepted Submission(s): 373


Problem Description
  威威猫十分迷恋篮球比赛,是忠实的NBA球迷,他常常幻想自己那肥硕的身躯也能飞起扣篮。另外,他对篮球教练工作也情有独钟,特别是对比赛的战术,投篮选择方面也是很有研究,下面就是威威猫研究过的一个问题:
  一场NBA篮球比赛总共48分钟,假如我们现在已经知道当前比分 A:B,A代表我方的比分,B代表对方的比分,现在比赛还剩下t秒时间。我们简单的认为双方各自进攻一次的时间皆固定为15秒(不到15秒则进攻不得分),且为交替进攻,即我方进攻一次,接着对方进攻,依次循环。
  进攻有三种选择方式:(这里不考虑命中率)
  1、造犯规,(假设都两罚一中)得1分;
  2、中距离投篮 得2分;
  3、三分球 得3分。
  为了简化问题,假设在对方回合,由于我方防守比较好,只让对手得1分,且为固定,即对方的进攻回合就为每回合得1分。现在比赛进入最后关头,接下来第一个回合是我方进攻,现在威威猫想要知道教练有多少种不同的选择能使我方可能赢得比赛(可能的意思就是不考虑命中率的情况)。
 

Input
输入有多组数据(不超过250组);
每组数据包含3个整数A,B和t,其中A和B 表示当前的比分(0 <= A, B <= 200),t表示还剩多少时间(单位秒 0 <= t <= 600)。
 

Output
请输出可行的方案数,每组数据输出占一行。
 

Sample Input
   
   
88 90 50
 

Sample Output
   
   
6
Hint
样例解析: 当前比分是88:90,还剩50秒则对方还最多有一次进攻机会(最后5秒进攻不成功),我方有两次,对方的最终得分将是91, 我方至少在两回合中拿到4分才能胜利,所以所有方案数是6种,即: 第一球 第二球 1 3 2 2 2 3 3 1 3 2 3 3
 






这道题抽象出来的大意就是:在t场中,有多少种得分方式,能够得到超过m分

转移方程为:dp[i][j]={dp[i-1][j-1]+dp[i-1][j-2]+dp[i-1][j-3]};

其实就是一个递推公式;

本来是考虑的一维数组,后面发现如果不保存前一场的状态,将无法得知要将哪些值相加。


代码:

#include<iostream>
#include<math.h>
using namespace std;
__int64 dp[50][105];
int main(){
    int n,m,t;
    while(scanf("%d%d%d",&n,&m,&t)!=EOF){
         memset(dp,0,sizeof(dp));
         int times=t/15;
         int between=m-n+times/2;
         if(times%2!=0)
             times=times/2+1;
         else
             times=times/2;
         int res=3*times;
         if(times==0){
                if(n>m)
                     cout<<"1"<<endl;
                else
                     cout<<"0"<<endl;
                continue;
                }
 //        cout<<res<<" "<<times<<" "<<between<<endl;
         dp[1][1]=1;
         dp[1][2]=1;
         dp[1][3]=1;
         for(int i=2;i<=times;i++){
                 for(int j=1;j<=times*3;j++){
                         if(j>=1){
                             dp[i][j]+=dp[i-1][j-1];
                             }
                         if(j>=2){
                             dp[i][j]+=dp[i-1][j-2];
                             }
                         if(j>=3){
                             dp[i][j]+=dp[i-1][j-3];
                             }
                             }
                             }
         __int64 sum=0;
         for(int k=between+1;k<=res;k++){
                 sum+=dp[times][k];
                 }
         cout<<sum<<endl;
         }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值