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);
}
玉蟾宫

 

posted @ 2019-10-08 17:10  _JSQ  阅读(117)  评论(0编辑  收藏  举报