dp基础

传球游戏
题目描述:
上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。
游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没传出去的那个同学就是败者,要给大家表演一个节目。
聪明的小蛮提出一个有趣的问题:有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了m次以后,又回到了小蛮手里。两种传球方法被视作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有3个同学1号、2号、3号,并且假设小蛮为1号,球传了3次回到小蛮手里的方式有1->2->3->1和1->3->2->1,共2种。
输入格式
输入文件ball.in共一行,有两个用空格隔开的整数n,m
40%的数据满足:3<=n<=30,1<=m<=20
100%的数据满足:3<=n<=30,1<=m<=30
输出格式
输出文件ball.out共一行,有一个整数,表示符合题意的方法数。
样列输入
3 3
样列输出
2

分析:
题目分析:

其实我觉得这跟上台阶是很相似的。也就是第 i 个 是由第 i-1 个 和 第 i+1个转移而来。
很明显就是一个动态规划了吧。
我想想
于是我们就知道状态转移方程了

dp[i][j] 表示 在i 个人 j 次传
然后 dp[i][j] = dp[i-1][j-1] + dp[i+1][j-1] 由于是个圈
不想加倍了……取个模 dp[i][j] = dp[(i-1+n)%n][j-1] + dp[(i+1)%n][j-1];
就可以了

int main(){
  readdata();//这个都懂吧0.0
  dp[0][0] = 1; //i个人  j次传 
  for(int j=1 ;j<=m ;j++)
    for(int i=0 ;i<n ;i++)
    {
      dp[i][j] = dp[(i-1+n)%n][j-1] + dp[(i+1)%n][j-1]; 
    }  
  printf("%d",dp[0][m]);
  return 0;
 }

纯洁的买卖
题目描述:
ALEJ并不是财迷,但是作为纯洁党的伟大领袖,不挣钱,纯洁的事业怎么能坚持下去!纯洁党现在已经有M(1<=M<=100000)元经费了。ALEJ有一个富II代朋友,叫做HSW,HSW特别喜欢高价收藏一些餐巾纸(毛泽东用过的)、袜子(恐龙穿过的)、马桶垫(还珠格格坐过的)、红领巾(毛新宇戴过的)等等,总之,没有他不买的。因此ALEJ想通过HSW这个大财主,去实现倒买倒卖拥有尽可能多的钱。
有N(1<=N<=100)件物品供他选择,
ALEJ每件物品的买入价为ci元,
HSW的收藏价为ri元。
每向HSW卖出一件物品i之后,还要向政府上交c[i]元的税。每种物品的数量都是无限的。
ALEJ想知道,通过一次买卖(种类、数量没有限制)后,纯洁党的经费能有多少。
输入格式
第一行两个整数,N,M。之后N行,每行两个整数:c[i],r[i]
输出格式
一个整数,表示一次买卖后手里最多的钱数。
样例输入
3 17
2 4
5 6
3 7
样例输出
22

分析:
很明显啊,完全背包问题。不多赘述。….
然后记住要交税……所以w[i] = r[i] - 2*c[i] (这里很关键,必须减2倍);
你只能求得利润
如果你说只减一倍不加本金…..那就大错特错了你一定想得通。。当然也许有蒙对的可能….但几率为0,因为我试了一下……..
所以必须 那样做 求得最大利润 + M (本金).

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;


inline int read() //其实这题不优化也行
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int m,n;
int c[110],w[110],r;
int f[100005];

void readdata(){
  n = read();m = read(); 
  for(int i = 1 ;i<=n ;i++)
  {
    c[i] = read();
    r=read();
    w[i] = r-2*c[i];
  }
}
//完全背包 
int main(){
 readdata();
  for(int i=1 ;i<=n ;i++) 
     for(int j=c[i];j<=m ;j++)
     {
      f[j] = max(f[j] , f[j-c[i]] + w[i]);
     }
 printf("%d",f[m]+m);
 return 0;
}

多边形游戏
题目描述
多边形游戏是一种在一个具有n个顶点的多边形上进行的游戏。每个顶点有个权值(整数)。如图1是一个n=4对应多边形,每个顶点上都有一个整数,每条边都有一个运算符+或者*,所有边按从1到n进行编号。
这里写图片描述
游戏都首先移除一条边,接下来可以进行如下操作:
选择一条边E和与之相关联的点V1和V2,用一个新的点替换它们,新点上的整数为V1,V2上的整数用E上的操作符运算后的结果。
没有边时游戏结束,游戏得分就是最后剩下的那个顶点上的整数。
对于图1中的多边形,如果游戏者首先去掉3,然后依次去掉1、4、2,最后得分将是0。
这里写图片描述
请你写一个程序,对于给定的多边形,计算出可能得到的最高分,并列出第一步移除哪些边可以得到这个最高分。
关于输入
输入第一行是一个正整数n(3<=n<=50),表示多边形的边数。
接下来一行是这个多边形的描述,包含n条边和n个顶点,加号边用t表示,乘号边用x表示,顶点用顶点上标的整数表示,输入按照边的编号从1到n的顺序给出。
关于输出
第一行输出可能得到的最高分。
第二行输出一些边的列表,只有第一步移除的边在这个列表中才可能得到最高分。每条边都用这个边上的编号表示,列表必须是升序的。
例子输入
4
t -7 t 4 x 2 x 5
例子输出
33
1 2

分析:
我想说这真的是一道要写很多的动归
原因在于 负负得正………
于是我们得有一个来存 max 一个存min (或者多加一维)
然后就是枚举去掉边 ,每种情况下的最大值
若为t;绝对值最大
最大数+最大数
最小数+最小数
若为x
正数*正数
负数*负数
正数*负数
负数*正数
然后对于dpmax[][] 在p[] 存下可能的办法 详见代码。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define LL long long
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
#define inf 0x3f3f3f3f

int dpmin[110][110],dpmax[110][110];
//   第 i 和 j 个数运算
int v[110];
char op[110];
int main()
{   freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    int n;
    int i,j,k;
    mem(dpmin,inf);
    mem(dpmax,-inf);
    scanf("%d",&n);
    getchar();
    for(i=1;i<=n;i++){
            scanf("%c %d",&op[i],&v[i]);
            getchar();
            op[i+n]=op[i];
            v[i+n]=v[i];
        }
        for(i=1;i<=2*n;i++)
          {
              dpmax[i][i]=dpmin[i][i]=v[i];
              if(i<2*n){
                if(op[i+1]=='t')
                  dpmax[i][i+1]=dpmin[i][i+1]=v[i]+v[i+1];
                else
                  dpmax[i][i+1]=dpmin[i][i+1]=v[i]*v[i+1];
              }
          }

        for(k=3;k<=n;k++) // k区间的长度
            for(i=1;i+k-1<=2*n;i++){
                    dpmax[i][i+k-1]=-inf;
                    dpmin[i][i+k-1]=inf;
                for(j=i;j<i+k-1;j++){
                    if(op[j+1]=='t'){
                       dpmax[i][i+k-1]=max(dpmax[i][i+k-1],dpmax[i][j]+dpmax[j+1][i+k-1]);

                       dpmin[i][i+k-1]=min(dpmin[i][i+k-1],dpmin[i][j]+dpmin[j+1][i+k-1]);
                    }
                    else{
                       dpmax[i][i+k-1]=max(dpmax[i][i+k-1],dpmax[i][j]*dpmax[j+1][i+k-1]);
                       dpmax[i][i+k-1]=max(dpmax[i][i+k-1],dpmin[i][j]*dpmin[j+1][i+k-1]);
                       dpmax[i][i+k-1]=max(dpmax[i][i+k-1],dpmax[i][j]*dpmin[j+1][i+k-1]);
                       dpmax[i][i+k-1]=max(dpmax[i][i+k-1],dpmin[i][j]*dpmax[j+1][i+k-1]);

                       dpmin[i][i+k-1]=min(dpmin[i][i+k-1],dpmax[i][j]*dpmax[j+1][i+k-1]);
                       dpmin[i][i+k-1]=min(dpmin[i][i+k-1],dpmax[i][j]*dpmin[j+1][i+k-1]);
                       dpmin[i][i+k-1]=min(dpmin[i][i+k-1],dpmin[i][j]*dpmax[j+1][i+k-1]);
                       dpmin[i][i+k-1]=min(dpmin[i][i+k-1],dpmin[i][j]*dpmin[j+1][i+k-1]);
                    }

                }
            }
        int p[55],cnt=0;
        LL ans=-inf;

        for(i=1;i<=n;i++){
            if(dpmax[i][i+n-1]>ans){
                ans=dpmax[i][i+n-1];
                cnt=0;
                p[cnt]=i;
            }else  if(dpmax[i][i+n-1]==ans){
                p[++cnt]=i;
            }
        }
        printf("%I64d\n",ans);
        for(i=0;i<cnt;i++)
            printf("%d ",p[i]);
        printf("%d\n",p[i]);
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值