DP五十题

目录

板子

范围

集合

转移

终态

初始化

P1474 货币系统 Money Systems

完全背包求方案数

不限制每种货币取的个数,则需要使用完全背包,这里是统计方案数,根据加法原理(分步相加)得出转移方程:f[j]=f[j-a[i]].

记得初始化f[0]=1;


P1233 木棍加工

思想:归约:按照第一个关键字从大到小排序后就只用考虑另一个关键字的降序问题;

SOL:

​ 要求的是第二个关键字的最长下降子序列的最小个数,根据导弹拦截第二问用到的定理Dilworth定理:最少的下降序列个数就等于整个序列最长上升子序列的长度,求一遍LIS,因为n<=5e3,我就没用nlogn,\(n^2\)跑一边就好了

const int N=5e3+10;
struct qujian{
    int a,b;
    bool operator <(const qujian &rhs)const{
        if(a!=rhs.a)return a>rhs.a;
        return b>rhs.b;
    }
}mg[N];

int f[N],maxx,n;

#undef int
int main(){
#define int long long
    #ifdef WIN32
    freopen("c.txt","r",stdin);
    #endif
    rd(n);
    rep(i,1,n){
        rd(mg[i].a),rd(mg[i].b);
    }
    sort(mg+1,mg+n+1);
    rep(i,1,n){
        f[i]=1;
        rep(j,1,i-1)
            if(mg[j].b<mg[i].b)
                f[i]=max(f[i],f[j]+1);
        maxx=max(maxx,f[i]);
    }
    printf("%lld\n",maxx);
    return 0;
}

好吧,还是悄咪咪复习一下O(nlogn)的做法

	f[++len]=mg[1].b;
    rep(i,2,n){
        if(mg[i].b>f[len])f[++len]=mg[i].b;
        else{
            int p=lower_bound(f+1,f+len+1,mg[i].b)-f;//求按第二关键字的LIS
            f[p]=mg[i].b;
        }
    }
    printf("%lld\n",len);

P2904 USACO08MAR跨河River Crossing

【前置技能】:语文足够好

【独立思考+1A】

设f[i]表示1~i只牛全部运输过去的最小花费

预处理sum[i]:运i只牛的总花费

转移方程:f[i]=min(f[i],f[i-j]+sum[j]+M)//加M只因为FJ还要回到对岸

记得最后答案再减去一个M,因为最后一次到对岸的时候FJ已经不用再去接Bessie们了

const int N=2510;
int m[N],sum[N],f[N];
int n,M,cnt;

int main(){
    #ifdef WIN32
    freopen("c.txt","r",stdin);
    #endif
    rd(n),rd(M);
    sum[0]=M;
    rep(i,1,n){
        rd(m[i]);
        sum[i]=sum[i-1]+m[i];
    }
    mem(f,0x3f);
    f[0]=0;
    rep(i,1,n){
        rep(j,1,i){
            f[i]=min(f[i],f[i-j]+sum[j]+M);
        }
    }
    printf("%d",f[n]-M);
    return 0;
}

P1336 最佳课题选择

范围:1e2的级别->\(O(n^3)\)的算法

集合:考虑物尽其用,给了m个课题就一个一个课题的去更新,记录前i个课题完成了j篇论文的最小时间。那么转移就可以根据m的阶段来划分,从m的上一个阶段递推。

转移:这道题的转移方程挺容易想的:f[i][j]=min(f[i][j],f[i−1][j−k]+a[i]∗k^b[i])

终态f[m][n]

初始化:根据注意到i-1可能为0,那转移就从这里开始。

const int N=210;
int f[N][N];
int n,m;
int a[N],b[N];

inline int ksm(int a,int k){
    int res=1;
    for(;k;k>>=1){
        if(k&1)res=res*a;
        a=a*a;
    }
    return res;
}

#undef int
int main(){
#define int long long
    #ifdef WIN32
    freopen("c.txt","r",stdin);
    #endif
    rd(n),rd(m);//n:论文数,m:课题数
    rep(i,1,m)rd(a[i]),rd(b[i]);
    rep(i,1,n)f[0][i]=INT_MAX;
    rep(i,1,m)
        rep(j,1,n){
            f[i][j]=INT_MAX;
            rep(k,0,j)
                f[i][j]=min(f[i][j],f[i-1][j-k]+a[i]*ksm(k,b[i]));
        }
    printf("%lld\n",f[m][n]);
    return 0; 
}

P2285 HNOI2004打鼹鼠

很妙的LIS!!!

朴素DP方法:f[i][j][k]机器人第k时刻在i,j的最大值,but!1e3的n和1e4的m直接炸掉!

范围:1e4的级别->\(O(n^2)\)的算法

集合:发现地鼠的转移随时间递增(根据LIS的思想),能从上个地鼠转移到此时刻的地鼠的条件是在时间间隔内可以移动俩点的曼哈顿距离。

转移

if(abs(x[i]-x[j])+abs(y[i]-y[j])<=t[i]-t[j])
                f[i]=max(f[i],f[j]+1);

终态:记录f[i]的最大值

初始化:f[i]=1

但是此题不可以为了追求O(nlogn)而用 LIS 的优化,因为此题的序列没有传递性

const int N=1e4+10;
int x[N],y[N],t[N];
int f[N];
int n,m,maxx;

#undef int
int main(){
#define int long long
    #ifdef WIN32
    freopen("c.txt","r",stdin);
    #endif
    rd(n),rd(m);
    rep(i,1,m){
        rd(t[i]),rd(x[i]),rd(y[i]);
    }
    rep(i,1,m){
        f[i]=1;
        rep(j,1,i-1){
            if(abs(x[i]-x[j])+abs(y[i]-y[j])<=t[i]-t[j])
                f[i]=max(f[i],f[j]+1);
        }
        maxx=max(maxx,f[i]);
    }
    printf("%lld\n",maxx);
    return 0; 
}

P1063 能量项链

范围:1e2的级别->\(O(n^3)\)的算法。区间DP的标准复杂度。

集合f[l][r]表示目前已经合并l->r这段区间的最大值

转移f[l][r]=max(f[l][r],f[l][k]+f[k][r]+a[l]*a[k]*a[r]);

终态max{f[i][i+n]},这是个环形DP

初始化f[i][i]=0但是我懒得打。

int f[405][405];
int n,a[205]; 

int main(){
    #ifdef WIN32
    freopen("a.txt","r",stdin);
    #endif
    rd(n);
    rep(i,1,n)
        rd(a[i]),a[i+n]=a[i];
    rep(len,1,n)
        for(int l=1;l+len<=2*n;++l){
            int r=l+len;
            rep(k,l+1,r-1)
                f[l][r]=max(f[l][r],f[l][k]+f[k][r]+a[l]*a[k]*a[r]);
        }
    int res=0;
    rep(i,1,n)
        res=max(res,f[i][n+i]);
    printf("%d",res);
    return 0;
}

P1077 摆花

因为是求方案数,我们考虑当前这一种方案的上一个状态是什么。第i种花可以取k:0~a[ i ]个,那么前i-1种花的方案数有f[i-1][j-k]种,每一种都可以转移到当前状态,根据加法原理,直接相加。

范围:1e2的级别->\(O(n^3)\)的算法。区间DP的标准复杂度。

集合f[i][j] 摆完前i种花共摆了j盆的方案。

转移:见代码

终态:RT,f[n][m]

初始化f[0][0]=1

const int N=1e3+10;
const int mod=1000007;

int f[N][N];//摆完前i种花共摆了j盆的方案。
int a[N],n,m;

int main(){
    rd(n),rd(m);
    rep(i,1,n)rd(a[i]);
    f[0][0]=1;
    rep(i,1,n)
        rep(j,0,m)
            rep(k,0,a[i])
                if(j-k>=0)
                    f[i][j]=(f[i][j]+f[i-1][j-k])%mod;
    printf("%d",f[n][m]%mod);
    return 0;
}

P1417 烹调方案

算法:01背包的带权变形,很显然嘛,如果不考虑t的影响,这题就是个裸的01背包,那么我们考虑t这个权值妖艳在哪里;

现在考虑相邻的两个物品x,y。假设现在已经耗费p的时间,那么分别列出先做x,y的代价:

a[x]-(t+c[x])* b[x]+a[y]-(t+c[x]+c[y])*b[y] (①)

a[y]-(t+c[y])* b[y]+a[x]-(t+c[y]+c[x])*b[x] (②)

对这两个式子化简,得到①>②的条件是c[x]* b[y]<c[y]*b[x].

发现只要满足这个条件的物品对(x,y),x在y前的代价永远更优。

因此可以根据这个条件进行排序,之后就是简单的01背包了。

注意开long long,会爆int!!!见祖宗了呢

const int N=55;

struct data{
    int a,b,c;
    bool operator <(const data &rhs)const {
        return b*rhs.c>c*rhs.b;
    }
}x[N];

int T,n,ans;
int f[100010];

#undef int
int main(){
#define int long long
    rd(T),rd(n);
    rep(i,1,n){rd(x[i].a);}
    rep(i,1,n){rd(x[i].b);}
    rep(i,1,n){rd(x[i].c);}
    sort(x+1,x+n+1);
    rep(i,1,n)
        dwn(j,T,x[i].c){
            f[j]=max(f[j],f[j-x[i].c]+x[i].a-x[i].b*j);
            ans=max(ans,f[j]);
        }
    printf("%lld",ans);
    return 0;
}

P1412 经营与开发

const int N=1e5+10;
int op[N],x[N];
double f[N];
int n,k,c,w;

int main(){
    rd(n),rd(k),rd(c),rd(w);
    rep(i,1,n){
        rd(op[i]),rd(x[i]);
    }
    dwn(i,n,1){
        if(op[i]==1)
            f[i]=max(f[i+1],f[i+1]*(1-0.01*k)+x[i]);
        else 
            f[i]=max(f[i+1],f[i+1]*(1+0.01*c)-x[i]);
    }
    printf("%.2lf",f[1]*w);
    return 0;
}
posted @ 2019-10-11 21:58  设计涉及社稷  阅读(164)  评论(0编辑  收藏  举报