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 */
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 */
还有一种方法叫悬线法。代码比较好理解
#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); }