[DP训练]rqnOJ

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[DP训练]rqnOJ相关的知识,希望对你有一定的参考价值。

P188 购物问题

 

题目梗概:

n个物品,其中每个物品价格xi,但是某两个物品不能同时购买。

问最大的价格是多少?

 

思考与理解:

一开始并没有想到树形背包DP,只是一直在想是不是分组背包~

在之后瞅了瞅题解的思路之后,恍然大悟。

先把有限制的物品之间的关系转换为父子关系就可以进行DP了。

对于每个有限制的物品要不选 要么不选 选的话会有什么结果 不选的话有什么结果。

如果没有限制的话 那么肯定是要买的~

技术分享
#include <cstdio>
#include <algorithm>
#include <cstring>
#define up(a,b,c) for(register int c=a;c<=b;++c)
int n,m;
int dp[1005][3];
int maodun[1005][1005],value[1005],ans,son[1005][1005];
bool used[1005];

void dfs(int x){
    used[x] = true;
    up(1,maodun[x][0],i){
        if(!used[maodun[x][i]]){
            son[x][++son[x][0]] = maodun[x][i];
            dfs(maodun[x][i]);
        }
    }
}

void RunDp(int x){
    if(son[x][0]==0){
        dp[x][1]=value[x];
        dp[x][0]=0;
    }
    else{
        up(1,son[x][0],i) RunDp(son[x][i]);
        up(1,son[x][0],i){
            int v = son[x][i];
            dp[x][1]+=dp[v][2];
            dp[x][2]+=std::max(dp[v][1],dp[v][2]);
        }
        dp[x][1]+=value[x];
    }
}

int main(){
    scanf("%d%d",&n,&m);
    up(1,n,i) scanf("%d",&value[i]);
    up(1,m,i){
        int x,y;
        scanf("%d%d",&x,&y);
        maodun[x][++maodun[x][0]] = y;
        maodun[y][++maodun[y][0]] = x;
    }
    up(1,n,i){
        if(maodun[i][0]==0){
            ans+=value[i];
            used[i] = 1;
        }
        else{
            if(!used[i]){
                dfs(i);
                RunDp(i);
                ans+=std::max(dp[i][1],dp[i][2]);
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}
P188

 

P201 奥运大包围

 

题目梗概:

(题面什么鬼)问题就是求一个环从某个地方断开,然后求LIS,取LIS最小值即可.

 

思考:

LIS挺简单的,但必须用nlogn的二分做法,否则n^3会TLE。

技术分享
#include<string.h>  
#include<stdio.h>  
#include<iostream>  
#include<algorithm>  
using namespace std;  
int dp[10001];  
int num[10001];  
int a[20001];  
int nums;  
int ans;  
void cha(int x)  
{  
    if(nums==0||x>=num[nums-1])  
    {  
        num[nums++]=x;  
        return ;  
    }  
    int l,r,mid;  
    l=0,r=nums;  
    mid=(l+r)/2;  
    while(l<r)  
    {  
        if(num[mid]<=x)l=mid+1;  
        else if(num[mid]>x) r=mid;  
        mid=(l+r)/2;  
    }  
    num[mid]=x;  
}  
void dos(int l,int r)  
{  
    nums=0;  
    for(int i=l; i<r; i++)  
    {  
        cha(a[i]);  
    }  
    ans=min(ans,nums);  
}  
int main()  
{  
    int n,i;  
    scanf("%d",&n);  
    ans=n;  
    for(i=0; i<n; i++)  
    {  
        scanf("%d",&a[i]);  
    }  
    for(i=n; i<2*n; i++)  
    {  
        a[i]=a[i-n];  
    }  
    for(i=0; i<n; i++)  
    {  
        dos(i,i+n);  
    }  
    cout<<ans<<endl;  
    return 0;  
}  
P201

 

P202 奥运火炬登珠峰

 

题面梗概:

n个物品,每个物品都有一个x氧气含量,y氮气含量,z重量。

给出保障生命安全的a,b,问z的最小重量是多少。

 

思考:

普通的一个二位费用背包,没啥说的。只不过需要把上限a,b开大点。毕竟选的x,y可以超过这个下限a,b

技术分享
#include <cstdio>
#include <algorithm>
#include <cstring>

int dp[233][1005];
int A,T,n;
int O[1005],N[1005],G[1005];
int Out=0x3f3f3f3f;

int main(){
    memset(dp,0x3f3f3f3f,sizeof(dp));
    scanf("%d%d",&A,&T);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d%d",&O[i],&N[i],&G[i]);
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=A+A;j>=O[i];j--){
            for(int k=T+T;k>=N[i];k--){
                dp[j][k]=std::min(dp[j][k],dp[j-O[i]][k-N[i]]+G[i]);
                //Out=std::min(Out,dp[j][k]);
            }
        }
    }
    for(int i=A+A;i>=A;i--){
        for(int j=T+T;j>=T;j--){
            Out=std::min(Out,dp[i][j]);
            //if(dp[i][j]==129) printf("%d %d\n",i,j);
        }
    }
    
    printf("%d\n",Out);
    return 0;
}
P202

 

P671 纯洁的买卖

题面梗概:

猪脚有m元经费,他会以x买入某件物品,以y的价格卖出。问最后会有多少钱。

 

思考:

背包问题,只不过需要处理一下xy的关系。

技术分享
#include <cstdio>
#include <algorithm>
#include <cstring>

int n,m;
int dp[1000005];
int x,y;

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x,&y);
        y-=x*2;
        for(int j=x;j<=m;j++){
            if(j-x>=0)dp[j]=std::max(dp[j],dp[j-x]+y);
            //printf("dp[%d]:%d\n",j,dp[j]);
        }
    }
    printf("%d\n",dp[m]+m);
    return 0;
}
P671

 

 

 

P57 找啊找啊找GF

 

题面梗概:

n个MM,每个MM需要花费rmb,rp,time,猪脚有m块RMB,r的人品值。

在保证MM数量的前提下,最小花费时间是多少。

 

思考:

一开始想着只用一个二维费用背包搞,记录MM数量最大是多少,然后在这个数量中查找一个最小值时间。

但是发现莫名其妙WA了,看了看题解。

发现做法很妙:

如果MM数量多,就保留多的。

如果MM数量一样,那么久保留时间短的。

技术分享
#include <cstdio>
#include <cstring>
#include <algorithm>

int n,m,r;
int dp[105][105];
int TIME[105][105];
int x[105],y[105],z[105];
int Min=0x3f3f3f3f;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&x[i],&y[i],&z[i]);
    }
    scanf("%d%d",&m,&r);
    for(int i=1;i<=n;i++){
        for(int j=m;j>=x[i];j--){
            for(int k=r;k>=y[i];k--){
                if(dp[j][k] < dp[j-x[i]][k-y[i]]+1){
                dp[j][k] = dp[j-x[i]][k-y[i]]+1;
                TIME[j][k] = TIME[j-x[i]][k-y[i]]+z[i];
                }
                if(dp[j][k] == dp[j-x[i]][k-y[i]]+1 && TIME[j][k] > TIME[j-x[i]][k-y[i]]+z[i]){
                TIME[j][k] = TIME[j-x[i]][k-y[i]]+z[i];
                }
                //printf("dp[%d][%d]:%d\n",j,k,dp[j][k]);
            }
        }
    }
    printf("%d",TIME[m][r]);
    return 0;
}
p57

 

 

P140 分配时间

题面梗概:

n个科目,t时间。

每个科目花费不同的时间可以得到不同的分数,但每门科目需要之前写名字花费x时间。

问最多可以拿到几分?

 

思考:

分组背包,只不过需要特出处理一下写名字的时间。

技术分享
#include <cstdio>
#include <algorithm>
#include <cstring>

int T,n,name;
int num[23][233];
int dp[233333];

int main(){
    scanf("%d%d%d",&T,&n,&name);
    //读入 
    for(int i=1;i<=n;i++){
        for(int j=1;j<=T;j++){
            scanf("%d",&num[i][j+name]);
        }
    }
    
    for(int k=1;k<=n;k++){
        for(int i=T;i>=name;i--){
            for(int j=i;j>=name;j--){
                dp[i] = std::max(dp[i],dp[i-j]+num[k][j]);
            }
        }
    }
    
    printf("%d\n",dp[T]);
    return 0;
} 
P140

 

 

 

 

 

P169 最小乘车费用

题面梗概:

每个车最多可以行驶10公里,行驶1,2,3,4...10公里的费用各不相同。

问行驶x公里的,最小费用是多少。

 

思考:

背包问题.

技术分享
#include <cstdio>
#include <algorithm>
#include <cstring>

int n,num[105];
int dp[233];

int main(){
    memset(dp,0x3f3f3f3f,sizeof(dp));
    for(int i=1;i<=10;i++) scanf("%d",&num[i]),dp[i]=num[i];
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=10;j++){
            if(i>j) dp[i]=std::min(dp[i],dp[i-j]+num[j]);
            else break;
            //printf("dp[%d]:%d\n",i,dp[i]);
        }
    }
    printf("%d\n",dp[n]);
    return 0;
}
P169

 

 

 

 

P95 多多看DV(加强版)

 

题面梗概:

爷爷给出T单位时间,商店有n个碟片,叔叔必须买m个(不能多不能少),输入的每个碟片会花费x的时间,会得到y的快乐。

如果碟片没法在规定时间内看完输出0,否则输出今晚的总分.

 

思考:

题面需要转化一下,把时间当作一个物品属性。进行二维费用背包。

贪心的思考一下:如果n个物品中,m个物品的时间>T的话。直接输出0

技术分享
#include <cstdio>
#include <algorithm>
#include <cstring>


int n,limit,l;
int t[105],m[105],fuck[105],Can[105];
int dp[233333][233];

int main(){
    memset(dp,-0x3f3f3f3f,sizeof(dp));
    scanf("%d%d%d",&n,&limit,&l);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&t[i],&m[i]);
        Can[i]=t[i];
        fuck[i]=1;
    }
    std::sort(Can+1,Can+1+n);
    int ans=0;
    for(int i=1;i<=limit;i++){
        ans+=Can[i];
    }
    if(ans>l){
        printf("0\n");
        return 0;
    }
    for(int i=0;i<=l;i++) dp[i][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=l;j>=t[i];j--){
            for(int k=limit;k>=1;k--){
                dp[j][k] = std::max(dp[j][k],dp[j-t[i]][k-1]+m[i]);
            }
        }
    }
    printf("%d\n",dp[l][limit]);
    return 0;
}
P95

 

以上是关于[DP训练]rqnOJ的主要内容,如果未能解决你的问题,请参考以下文章

RQNOJ 188 购物问题:树形dp

RQNOJ 622 最小重量机器设计问题:dp

RQNOJ 328 炮兵阵地:状压dp

RQNOJ 311 [NOIP2000]乘积最大:划分型dp

RQNOJ 356 mty的格斗 dp

RQNOJ 514 字串距离:dp & 字符串