动态规划

基础DP

POJ 3176: Cow Bowling
数字三角形问题,DP方程不再赘述。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
数字三角形问题,DP方程不再赘述。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=350+5;
const ll INF=0x3f3f3f3f3f3f3f3fll;
int n,a[maxn][maxn];
ll d[maxn][maxn],ans;
int main() {
    ios::sync_with_stdio(false);
    //freopen("Cow Bowling.in","r",stdin);
    memset(d,-INF,sizeof(d));
    cin>>n;
    for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) cin>>a[i][j];
    d[1][1]=(ll)a[1][1];ans=(ll)a[1][1]; //注意这里ans不能初始化为-INF,否则n=1的情况会出问题 
    for(int i=2;i<=n;i++){
        for(int j=1;j<=i;j++){
            if(j-1>=1) d[i][j]=max(d[i-1][j-1],d[i-1][j])+a[i][j];
            else d[i][j]=d[i-1][j]+a[i][j];
            if(i==n) ans=max(ans,d[n][j]);
//          cout<

POJ 2229: Sumsets
题意:给出一个整数n,求解该整数n有多少种由2的幂次之和组成的方案。
法一可以直接分析出递推式。
解题思路:
1.可以将n用二进制表示。
n=1,只有1种表示方法。
n=2,10(2),二进制表示下,可以分拆成{1,1},{10}有两种表示方法。
n=3, 11(2),可以分拆成{1,1,1},{10,1}。
n=4, 100(2),{1,1,1,1},{10,1,1},{10,10},{100}。
总结:如果所求的n为奇数,那么所求的分解结果中必含有1,因此,直接将n-1的分拆结果中添加一个1即可 为s[n-1]。
如果所求的n为偶数,那么n的分解结果分两种情况
1.含有1 这种情况可以直接在n-1的分解结果中添加一个1即可,s[n-1]。
2.不含有1 那么,分解因子的都是偶数,将每个分解的因子都除以2,刚好是n/2的分解结果,并且可以与之一一对应,这种情况有s[n/2]。
法二用完全背包实现。
为了避免1+2+1和1+1+2这样的重复计数。
外层从小到大循环物品(这样小物品在前尝试,大物品在后尝试),内层正序循环则保证了完全背包。
代码如下

/*
题意:给出一个整数n,求解该整数n有多少种由2的幂次之和组成的方案.
*/
#define method_1 
#ifdef method_1
/*
法一可以直接分析出递推式。
解题思路:
1.可以将n用二进制表示。
n=1,只有1种表示方法。
n=2,10(2),二进制表示下,可以分拆成{1,1},{10}有两种表示方法。
n=3, 11(2),可以分拆成{1,1,1},{10,1}。
n=4, 100(2),{1,1,1,1},{10,1,1},{10,10},{100}。
总结:如果所求的n为奇数,那么所求的分解结果中必含有1,因此,直接将n-1的分拆结果中添加一个1即可 为s[n-1]。
如果所求的n为偶数,那么n的分解结果分两种情况
1.含有1 这种情况可以直接在n-1的分解结果中添加一个1即可,s[n-1]。
2.不含有1 那么,分解因子的都是偶数,将每个分解的因子都除以2,刚好是n/2的分解结果,并且可以与之一一对应,这种情况有s[n/2]。
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000000+5;
const int INF=0x3f3f3f3f;
const ll mod=1000000000ll;
//const int num[]={0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288};//a[1]~a[20]
int n;
ll d[maxn]={0},ans=0ll; //d[i]表示i分解的方案数
int main() {
    ios::sync_with_stdio(false);
    //freopen("Sumsets.in","r",stdin);
    /*
    int base=1;
    for(int i=1;i<=20;i++) base*=2,cout<>n;
    d[1]=1ll;
    for(int i=2;i<=n;i++){
        if(i&1) d[i]=d[i-1];
        else d[i]=(d[i-1]+d[i/2])%mod;
//      D(d[i]);
    }
    cout<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000000+5;
const int INF=0x3f3f3f3f;
const ll mod=1000000000ll;
const int num[]={0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288};//a[1]~a[20]
int n;
ll d[maxn]={0},ans=0ll; //d[i]表示i分解的方案数
int main() {
    ios::sync_with_stdio(false);
    //freopen("Sumsets.in","r",stdin);
    /*
    int base=1;
    for(int i=1;i<=20;i++) base*=2,cout<>n;
    d[0]=d[1]=1ll;
    for(int i=1;i<=20;i++){
        for(int j=2;j<=n;j++){
            if(j>=num[i]) d[j]+=d[j-num[i]];
//          d[j]%=mod; 这样写会比下面这一行慢上一倍 
            while(d[j]>=mod) d[j]-=mod;
        }
    }
    cout<

POJ 2385: Apple Catching
d[i,j]表示到第i分钟,移动了j次后获得的的最多苹果数。显然根据j的奇偶性可以直接知道bessie所在的苹果树。
初值:。其他也都为0。
目标:。
转移方程:每一阶段都有两种选择,一种是移动,一种是不移动。如果要判断i时刻移动了j次后是否能接到苹果,只要用即可。

代码如下

/*

*/
#define method_1
#ifdef method_1
/*
d[i,j]表示到第i分钟,移动了j次后获得的的最多苹果数。显然根据j的奇偶性可以直接知道bessie所在的苹果树。 
初值:d[0,0]=0。其他也都为0。
目标:max{d[t,i]},0<=i<=w。
转移方程:每一阶段都有两种选择,一种是移动,一种是不移动。如果要判断i时刻移动了j次后是否能接到苹果,只要用(j&1)==(a[i]-1))即可。
即d[i,j]=max{d[i-1,j-1],d[i-1,j]+(j&1)==(a[i]-1))}
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxt=1000+5;
const int maxw=30+5;
const int INF=0x3f3f3f3f;
int d[maxt][maxw],t,w,a[maxt],ans;
int main() {
    ios::sync_with_stdio(false);
//  freopen("Apple Catching.in","r",stdin);
    cin>>t>>w;
    for(int i=1;i<=t;i++) cin>>a[i],a[i]--;
    for(int i=1;i<=t;i++){
        for(int j=0;j<=w;j++){
            if(j>=1) d[i][j]=max(d[i-1][j-1],d[i-1][j])+((j&1)==a[i]);
            else d[i][j]=d[i-1][j]+((j&1)==a[i]);
        }
    }
    for(int i=0;i<=w;i++) ans=max(ans,d[t][i]);
    cout<

POJ 3616: Milking Time
法一 对于每个区间[l,r]建边l->r+R。然后就在相邻的区间间建边,边权为0,保证顶点编号递增即可。
为了便于计算,额外建边0->第一个区间左端点和 最后一个区间右端点->n+r。
起点为0,终点n+r,跑最长路即可。
法二 将区间按照左端点升序排序,就是一个LIS问题。
代码如下

/*

*/
#define method_2
#ifdef method_1
/*
对于每个区间[l,r]建边l->r+R。然后就在相邻的区间间建边,边权为0,保证顶点编号递增即可。
为了便于计算,额外建边0->第一个区间左端点和 最后一个区间右端点->n+r。 
起点为0,终点n+r,跑最长路即可。
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000000*2+5;
const int maxm=1000+5;
const int INF=0x3f3f3f3f;
int n,m,r,vis[maxn],d[maxn],head[maxn],tot=1;
struct node{
    int from,to,c;
}edge[maxm<<2];
void add(int from,int to,int c){
    edge[++tot].from=head[from],head[from]=tot,edge[tot].c=c,edge[tot].to=to;
}
queueq;
void spfa(){
//  memset(d,INF,sizeof(d));
    memset(vis,0,sizeof(vis));
    d[0]=0;q.push(0);vis[0]=1;
    while(q.size()){
        int x=q.front();q.pop();vis[x]=0;
        for(int i=head[x];i;i=edge[i].from){
            int y=edge[i].to,c=edge[i].c;
            if(d[x]+c>=d[y]){
                d[y]=d[x]+c;
                if(!vis[y]){
                    q.push(y);
                    vis[y]=1;
                }
            }
        }
    }
//  for(int i=0;i<=n+r;i++) D(d[i]); 
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Milking Time.in","r",stdin);
    cin>>n>>m>>r;
    int u,v,c;
    for(int i=1;i<=m;i++){
        cin>>u>>v>>c;add(u,v+r,c);vis[u]=vis[v+r]=1;
    }
    u=1,v=0;
    while(1){
        while(!vis[u]&&u=n+r) break;
        v=u,u++;
    }
    spfa();
    cout<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000000*2+5;
const int maxm=1000+5;
const int INF=0x3f3f3f3f;
int n,m,r,d[maxm],ans=0;
struct node{
    int s,t,c;
    bool operator<(const node &h)const{return s>n>>m>>r;
    int u,v,c;
    for(int i=1;i<=m;i++){
        cin>>seg[i].s>>seg[i].t>>seg[i].c;
    }
    sort(seg+1,seg+m+1);
//  for(int i=1;i<=m;i++) d[i]=seg[i].c;
    for(int i=1;i<=m;i++){
        d[i]=seg[i].c;
        for(int j=1;j<=i-1;j++){
            if(seg[j].t+r<=seg[i].s) d[i]=max(d[i],d[j]+seg[i].c);
        }
        ans=max(ans,d[i]);
    }
    cout<

POJ 3280: Cheapest Palindrome
d[i,j]表示将[i,j]变成回文串的最小代价。
初值:d[i,i]=0,其他为INF。
目标:d[1,m]。
转移方程:
1,若a[i]==a[j],d[i,j]=d[i+1,j-1]
2,若a[i]!=a[j],d[i,j]=min{d[i+1,j]+in[a[i]],d[i+1,j]+del[a[i]],d[i,j-1]+in[a[j],d[i,j-1]+del[a[j]]}
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
d[i,j]表示将[i,j]变成回文串的最小代价。
初值:d[i,i]=0,其他为INF。 
目标:d[1,m]。 
转移方程:
1,若a[i]==a[j],d[i,j]=d[i+1,j-1]
2,若a[i]!=a[j],d[i,j]=min{d[i+1,j]+in[a[i]],d[i+1,j]+del[a[i]],d[i,j-1]+in[a[j],d[i,j-1]+del[a[j]]} 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=26+5;
const int maxm=2000+5;
const int INF=0x3f3f3f3f;
int n,m,d[maxm][maxm];
string a;
mapin,del;
void dp(){
    memset(d,INF,sizeof(d));
    for(int i=1;i<=m;i++) d[i][i]=d[i][i-1]=0;
    for(int len=2;len<=m;len++){
        for(int i=1;i<=m-len+1;i++){
            int j=i+len-1;
//          D(i);D(j);E;
            if(a[i]==a[j]) d[i][j]=d[i+1][j-1];
            else{
                int temp1=min(in[a[i]],del[a[i]]);
                d[i][j]=min(d[i+1][j]+temp1,d[i][j]);
                int temp2=min(in[a[j]],del[a[j]]);
                d[i][j]=min(d[i][j-1]+temp2,d[i][j]);
            }
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Cheapest Palindrome.in","r",stdin);
    cin>>n>>m;
    cin>>a;a.insert(0,"0");
    char ch;int c1,c2;
    for(int i=1;i<=n;i++){
        cin>>ch>>c1>>c2;
        in[ch]=c1,del[ch]=c2;
    }
    dp();
    /*
    for(int i=1;i<=m;i++){
        for(int j=i;j<=m;j++){
            D(d[i][j]);
        }
        E;
    }
    */
    cout<

优化递推式

POJ 1742: Coins
f[i]表示组成i元是否可行。
初值:f[0]=1
目标:所有f[i]=1的个数
转移方程:
设used[j]表示F[j]在阶段i时为1至少需要用多少枚第i种硬币。
若f[j-A[i]]=1,且used[j=A[i]] 代码如下

/*

*/
#define method_1
#ifdef method_1
/*
f[i]表示组成i元是否可行。
初值:f[0]=1
目标:所有f[i]=1的个数
转移方程:
设used[j]表示F[j]在阶段i时为1至少需要用多少枚第i种硬币。 
若f[j-A[i]]=1,且used[j=A[i]]
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100+5;
const int maxm=100000+5;
const int INF=0x3f3f3f3f;
int f[maxm],used[maxm],n,m,A[maxn],C[maxn],ans;
void init(){
    memset(f,0,sizeof(f));f[0]=1;ans=0;
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Coins.in","r",stdin);
    while(cin>>n>>m){
        init();
        if(n==0&&m==0) break;
        for(int i=1;i<=n;i++) cin>>A[i];
        for(int i=1;i<=n;i++) cin>>C[i];
        for(int i=1;i<=n;i++){
            memset(used,0,sizeof(used));
            for(int j=1;j<=m;j++){
                if(!f[j]&&j-A[i]>=0){
                    if(f[j-A[i]]==1&&used[j-A[i]]

POJ 3046: Ant Counting
法一,多重集组合数。
f[i,j]表示前i种蚂蚁,选出j个的方案数。直接跑多重背包+滚动数组。600msAC。
转移方程:
法二,对method_1的转移方程优化。
,同时注意出现负数的情况即可。
100msAC。
代码如下

/*

*/
#define method_2
#ifdef method_1
/*
多重集组合数。 
f[i,j]表示前i种蚂蚁,选出j个的方案数。直接跑多重背包+滚动数组。600msAC。 
转移方程:f[i,j]=∑f[i-1,j-k],0<=k<=min(j,num[i]) 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int maxa=100000+5;
const int INF=0x3f3f3f3f;
const int mod=1e6;
int t,a,s,b,num[maxn],f[2][maxa],tot=0;
int main() {
    ios::sync_with_stdio(false);
    //freopen("Ant Counting.in","r",stdin);
    cin>>t>>a>>s>>b;
    int temp;
    for(int i=1;i<=a;i++){
        cin>>temp;num[temp]++;
    }
    f[0][0]=1;
    for(int i=1;i<=t;i++){
        memset(f[i&1],0,sizeof(f[i&1])); //滚动数组这里要先清空 
        tot+=num[i];
        for(int j=0;j<=tot;j++){ //卡常数方式 这里将tot改成a就会TLE 
            for(int k=0;k<=min(j,num[i]);k++){
                f[i&1][j]=(f[i&1][j]+f[i-1&1][j-k])%mod;
            }
        }
    }
    int ans=0;
    for(int i=s;i<=b;i++) ans=(ans+f[t&1][i])%mod;
    cout<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int maxa=100000+5;
const int INF=0x3f3f3f3f;
const int mod=1e6;
int t,a,s,b,num[maxn],f[2][maxa];
int main() {
    ios::sync_with_stdio(false);
    //freopen("Ant Counting.in","r",stdin);
    cin>>t>>a>>s>>b;
    int temp;
    for(int i=1;i<=a;i++){
        cin>>temp;num[temp]++;
    }
    f[0][0]=f[1][0]=1; //这个转移方程中f[1,1]可能来自f[0,0],f[0,1](一定为0),f[1,0] 
    for(int i=1;i<=t;i++){
//      memset(f[i&1],0,sizeof(f[i&1])); //同理 滚动数组这里不要清空 
        for(int j=1;j<=a;j++){
            if(j-1-num[i]>=0) f[i&1][j]=f[i&1][j-1]-f[i-1&1][j-1-num[i]]+f[i-1&1][j];
            else f[i&1][j]=f[i-1&1][j]+f[i&1][j-1];
            f[i&1][j]=(f[i&1][j]+mod)%mod; //防止负数 
        }
    }
    int ans=0;
    for(int i=s;i<=b;i++) ans=(ans+f[t&1][i])%mod;
    cout<

POJ 3181: Dollar Dayz
完全背包即可。
但要高精度。
method_1结果为WA。
代码如下

/*

*/
#define method_2
#ifdef method_1
/*
要高精度。 
method_1结果为WA。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int maxk=100+5;
const int INF=0x3f3f3f3f;
int d[maxn],n,k;
int main() {
    ios::sync_with_stdio(false);
    //freopen("Dollar Dayz.in","r",stdin);
    cin>>n>>k;
    d[0]=1;
    for(int i=1;i<=k;i++) for(int j=i;j<=n;j++) d[j]+=d[j-i];
    cout<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int maxp=100+5;
const int INF=0x3f3f3f3f;
int n,k;
struct bigint{
    int a[maxp],n;
    int &operator[](int x){return a[x];} 
    bigint(){for(int i=0;i=1;i--)printf("%d",a[i]);} 
    friend bigint operator+(bigint x,bigint y){ 
        bigint res;res.n=x.n>y.n?x.n:y.n; int i; 
        for(i=1;i<=res.n;i++)res[i]=x[i]+y[i]; 
        for(i=1;i<=res.n;i++)res[i+1]+=res[i]/10,res[i]%=10; 
        if(res[res.n+1])res.n++; return res; 
    }
}d[maxn];
int main() {
    ios::sync_with_stdio(false);
    //freopen("Dollar Dayz.in","r",stdin);
    cin>>n>>k;
    /*
    d[0]=1;
    for(int i=1;i<=k;i++) for(int j=i;j<=n;j++) d[j]+=d[j-i];
    cout<

进阶DP

POJ 1065: Wooden Sticks
Dilworth定理
https://blog.csdn.net/litble/article/details/85305561
因此本题就是按l升序排序,若l相同则按w升序排序。然后对w求最长严格下降子序列(w1<=w2的反面是w1>w2)的长度
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
Dilworth定理
https://blog.csdn.net/litble/article/details/85305561
因此本题就是按l升序排序,若l相同则按w升序排序。然后对w求最长严格下降子序列(w1<=w2的反面是w1>w2)的长度 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=5000+5;
const int INF=0x3f3f3f3f;
int T,n,d[maxn],ans;
struct node{
    int l,w;
    bool operator<(const node& h)const{return (l>T;
    while(T--){
        cin>>n;
        init(n);
        for(int i=1;i<=n;i++) cin>>seg[i].l>>seg[i].w;
        sort(seg+1,seg+n+1);
        for(int i=1;i<=n;i++) for(int j=1;j

POJ 1631: Bridging signals
和Wooden Sticks原理类似,不过由于数据量较大,所以dp时要用nlogn的算法。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
和Wooden Sticks原理一样,不过由于数据量较大,所以dp时要用nlogn的算法。 
*/
#include
#include
#include
const int maxn=40000+10;
int n,T;
int d[maxn];//d[i]表示1~i中最长的不下降子序列的长度
struct node {
    int a,b;
} qj[maxn];
bool operator<(const node &a1,const node &a2) {
    if(a1.a!=a2.a) return a1.aans) { //插在了最后 说明答案可以更新
                ans++;
            }
//      d[i]=std::max(temp+1,d[i]);
        }
//  for(int i=1;i<=n;i++){
        //  std::cout<

POJ 3666: Making the Grade
题解链接https://www.jianshu.com/p/2c07da54278b
代码如下

/*
https://www.jianshu.com/p/2c07da54278b
*/
#define method_2
#ifdef method_1
/*

*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=2000+5;
const int INF=0x3f3f3f3f;
int n,a[maxn],b[maxn],f[maxn][maxn],cnt;
int main() {
    ios::sync_with_stdio(false);
    //freopen("Making the Grade.in","r",stdin);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    cnt=unique(b+1,b+n+1)-b-1;
    /*
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
    }
    */
    int ans=INF;
    memset(f,INF,sizeof(f));
    for(int i=1;i<=cnt;i++) f[0][i]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=cnt;j++){
            for(int k=1;k<=j;k++){
                f[i][j]=min(f[i-1][k]+abs(a[i]-b[j]),f[i][j]);
            }
            if(i==n) ans=min(ans,f[n][j]);
        }
    }
    cout<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=2000+5;
const int INF=0x3f3f3f3f;
int n,a[maxn],b[maxn],f[maxn][maxn],cnt;
int main() {
    ios::sync_with_stdio(false);
    //freopen("Making the Grade.in","r",stdin);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    cnt=unique(b+1,b+n+1)-b-1;
    /*
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
    }
    */
    int ans=INF;
    memset(f,INF,sizeof(f));
    for(int i=0;i<=cnt;i++) f[0][i]=0;
    for(int i=1;i<=n;i++){
        int val=f[i-1][0];
        for(int j=1;j<=cnt;j++){
            val=min(val,f[i-1][j]);
            f[i][j]=val+abs(a[i]-b[j]);
            if(i==n) ans=min(ans,f[n][j]);
        }
    }
    cout<

POJ 2392: Space Elevator
这道题因为每个点的限制不一样,所以需要从限制小的先进行dp(按照a[i]升序排序后dp)。
因为限制大的可以放在限制小的上面,但是限制小的无法再往大的上面堆。
f[i,j]表示使用前i个block,能否达到高度j。
初值:
目标:条件:,输出最大的满足条件的i
转移方程:
这个多重背包在实现时,可以省略f数组的第一维。同时由于c[i]范围很小,所以不用二进制或者单调队列优化。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
这道题因为每个点的限制不一样,所以需要从限制小的先进行dp(按照a[i]升序排序后dp)。 
因为限制大的可以放在限制小的上面,但是限制小的无法再往大的上面堆。 
f[i,j]表示使用前i个block,能否达到高度j。
初值:f[0,0]=1
目标:条件:f[n,i]=1,输出最大的满足条件的i 
转移方程:f[i,j]|=f[i-1,j-k*h[i]] j<=a[i],0<=k<=c[i]
这个多重背包在实现时,可以省略f数组的第一维。同时由于c[i]范围很小,所以不用二进制或者单调队列优化。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxk=400+5;
const int maxai=40000+5;
const int INF=0x3f3f3f3f;
int f[maxai],h[maxk],a[maxk],c[maxk],k;
struct node{
    int h,a,c;
    bool operator<(const node& h)const{return a>k;
    for(int i=1;i<=k;i++) cin>>block[i].h>>block[i].a>>block[i].c;
    sort(block+1,block+k+1);
    f[0]=1;
    for(int i=1;i<=k;i++){
        for(int j=1;j<=block[i].c;j++){ //循环数量放在第二重循环里头 
            for(int l=block[i].a;l>=block[i].h;l--){
                f[l]|=f[l-block[i].h]; //注意这里不是f[l-j*block[i].h]
            }
        }
    }
    for(int i=maxai-5;i>=0;i--){
        if(f[i]){
            cout<

POJ 2184: Cow Exhibition
01背包。
d[i,j]表示前i个奶牛,TS为j时最大的TF。(第一维可省略,第二维需要数组下标平移)
如果S是负值的话会导致重复计算,所以此时需要调转循环方向。
PS:这题深搜可过。只需要加两个剪枝即可。
剪枝一:s和f都大于等于0必选。
剪枝二:s和f都小于0必不选。
代码如下

/*
PS:这题深搜可过。只需要加两个剪枝即可。
剪枝一:s和f都大于等于0必选。 
剪枝一:s和f都小于0必不选。 
*/
#define method_1
#ifdef method_1
/*
01背包。 
d[i,j]表示前i个奶牛,TS为j时最大的TF。(第一维可省略,第二维需要数组下标平移) 
如果S是负值的话会导致重复计算,所以此时需要调转循环方向。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100+5;
const int base=100*1000+5;
const int INF=0x3f3f3f3f;
int n,s[maxn],f[maxn],d[base*2+1005],ans=0;
int main() {
    ios::sync_with_stdio(false);
    //freopen("Cow Exhibition.in","r",stdin);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>s[i]>>f[i];
    memset(d,-INF,sizeof(d));d[base]=0; 
    for(int i=1;i<=n;i++){
        if(s[i]>=0){
            //本来应该在[-base+s[i],base]之间dp,坐标平移后就变成了[s[i],base] 
            for(int j=base*2;j>=s[i];j--) d[j]=max(d[j],d[j-s[i]]+f[i]); 
        }
        else{
            for(int j=s[i];j<=base*2+s[i];j++) //注意这里不是for(int j=s[i];j<=base*2;j++) 这样j-s[i]就会到达不合法的区域 
                d[j]=max(d[j],d[j-s[i]]+f[i]); 
        }
    }
    for(int i=base;i<=base*2;i++){ //只有[base,base*2]是合法的 
        if(d[i]>=0) ans=max(ans,d[i]+i-base);
    }
    cout<

你可能感兴趣的:(动态规划)