zoukankan      html  css  js  c++  java
  • DP1 1008

    birthday

    有n个商品,第i个商品价值为Wi,买k个(k>0)可以送AI*k+Bi个糖,最大预算为m,求最多能赚多少糖。

    1 ≤ M ≤ 2000 1 ≤ N ≤ 1000
    0 ≤ Ai, Bi ≤ 2000 1 ≤ Wi ≤ 2000

    题解

    就是完全背包,最烦的就是不买糖就没有Bi,很难维护。

    考虑f[i][j]为选取前n中商品,花费j的最大糖果数,

    opt[j]为当前层f[i][j]是否选取了i商品,没选的话就比较选和不选,不然就是再选和不选和重新选。

    不过好像也可以不用opt,如果是重新选的话就从上一层转移就好了。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int maxn=2005;
    int n,m;
    int w[maxn],a[maxn],b[maxn];
    int f[maxn][maxn];
    bool opt[maxn];//当前重量为j的最优值是否装了物品i 
    
    template<class T>inline void read(T &x){
        x=0;int f=0;char ch=getchar();
        while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x = f ? -x : x ;
    }
    
    int main(){
        freopen("birthday.in","r",stdin);
        freopen("birthday.out","w",stdout);
        read(n);read(m);
        for(int i=1;i<=n;i++) read(w[i]),read(a[i]),read(b[i]);
        for(int i=1;i<=n;i++){
            memset(opt,false,sizeof(opt));
            for(int j=0;j<=m;j++) f[i][j]=f[i-1][j];
            for(int j=w[i];j<=m;j++){
                if(!opt[j-w[i]]){//没放 
                   if(f[i][j-w[i]]+a[i]+b[i]>f[i][j]){
                       f[i][j]=f[i][j-w[i]]+a[i]+b[i];
                       opt[j]=true;
                  }
              }
              else {
                  if(f[i][j-w[i]]+a[i]>f[i][j]){//再放 
                      f[i][j]=f[i][j-w[i]]+a[i];
                      opt[j]=true;
                    }
                    if(f[i-1][j-w[i]]+a[i]+b[i]>f[i][j]){//重新放 
                        f[i][j]=f[i-1][j-w[i]]+a[i]+b[i];
                        opt[j]=true;
                    }
                }
            }
        }
        printf("%d",f[n][m]);
    }
    /*
    2 90
    10 10 0
    20 5 20
    15 10 10
    */
    birthday

    question

    给出一个n行m列的01图,求最大全1的矩形。

    n,m<=1000

    题解

    先固定下界,可以预处理出这一层的点向上最大能到的高度,然后就和这个一样了

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int maxn=1005;
    int n,m,mp[maxn][maxn];
    int up[maxn][maxn];
    int l[maxn],r[maxn];
    int top,s[maxn];
    int ret;
    
    template<class T>inline void read(T &x){
        x=0;int f=0;char ch=getchar();
        while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x = f ? -x : x ;
    }
    
    void init(){
        read(n);read(m);
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
          read(mp[i][j]);
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
          if(mp[i][j]) up[i][j]=up[i-1][j]+1;
    }
    
    void solve(int i){
        top=0;
        s[++top]=1;
        for(int j=2;j<=m;j++){
            while(top&&up[i][j]<up[i][s[top]]) r[s[top--]]=j;
            s[++top]=j;
        }
        while(top) r[s[top--]]=m+1;
        s[++top]=m;
        for(int j=m-1;j;j--){
            while(top&&up[i][j]<up[i][s[top]]) l[s[top--]]=j;
            s[++top]=j;
        }
        while(top) l[s[top--]]=0;
        for(int j=1;j<=m;j++)
         ret=max(ret,up[i][j]*(r[j]-l[j]-1));
    }
    
    int main(){
        freopen("question.in","r",stdin);
        freopen("question.out","w",stdout);
        init();
        for(int i=1;i<=n;i++) solve(i);
        printf("%d",ret);
    }
    /*
    5 4
    1 0 1 1
    1 1 1 1
    1 0 0 0
    1 1 1 1
    1 0 0 0
    */
    question

    还有一种方法叫悬线法。代码比较好理解

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    const int maxn=1005;
    int n,m,ret;
    int mp[maxn][maxn];
    int l[maxn][maxn],r[maxn][maxn],up[maxn][maxn];
    char op[2];
    
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++){
              scanf("%s",op);
                mp[i][j]=op[0]=='F';
                up[i][j]=mp[i][j];
                l[i][j]=r[i][j]=j;
         }
        for(int i=1;i<=n;i++)
         for(int j=2;j<=m;j++)
          if(mp[i][j]&&mp[i][j-1])
           l[i][j]=l[i][j-1];
        for(int i=1;i<=n;i++)
         for(int j=m-1;j;j--)
          if(mp[i][j]&&mp[i][j+1])
           r[i][j]=r[i][j+1];
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++){
              if(i>1&&mp[i][j]&&mp[i-1][j]){
                   l[i][j]=max(l[i][j],l[i-1][j]);
                   r[i][j]=min(r[i][j],r[i-1][j]);
                   up[i][j]=up[i-1][j]+1;
                }
             ret=max(ret,(r[i][j]-l[i][j]+1)*up[i][j]);
         }
        printf("%d",ret*3);
    }
    玉蟾宫
  • 相关阅读:
    算法-经典趣题-寻找假银币
    一天一个 Linux 命令(3):cat 命令
    算法-经典趣题-青蛙过河
    常用数据库有哪些?
    SpringBoot2.0入门教程(一) 快速入门,项目构建HelloWorld示例
    一天一个 Linux 命令(2):ls 命令
    算法-经典趣题-爱因斯坦阶梯问题
    一天一个 Linux 命令(1):vim 命令
    什么是开发环境、测试环境、UAT环境、仿真环境、生产环境?
    算法-经典趣题-渔夫捕鱼
  • 原文地址:https://www.cnblogs.com/sto324/p/11636616.html
Copyright © 2011-2022 走看看