[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

 

P390 地震了

题面梗概:

猪脚要下楼,

n层楼 初始速度v 最多只能比别人快k,

再输入第2层到第n层的速度限制,

若能下楼,输出速度之和除以(N-1),保留两位小数。

若不能,给涛哥发个短信吧,输出“YI DING YAO JIAN CHI JI HUA SHENG YU”(不含引号)

 

思考:

状态挺好思考的,在当前楼层n 速度为v,你只能向(n-1,v) (n-1,v-1) (n-1,v+1)三个位置转移.

坑点是要特判一下在n层时,速度是否能满足当前楼层要求。

#include <cstdio>
#include <algorithm>
#include <cstring>

int dp[105][205];
int n,v,k,limit[105],Max;
double tot;


int main(){
    scanf("%d%d%d",&n,&v,&k);
    dp[1][v]=v;
    for(int i=n-1;i>=1;i--){
        scanf("%d",&limit[i]);
    }
    if(v-k>limit[1] ||  v<limit[1]){
        printf("YI DING YAO JIAN CHI JI HUA SHENG YU");
        return 0;
    }
    for(int i=2;i<n;i++){
        for(int j=0;j<203;j++){
            if(j-limit[i]<=k && j>=limit[i]) dp[i][j] = std::max(dp[i-1][j],std::max(dp[i-1][j-1],dp[i-1][j+1]));
            else dp[i][j]=0;
            if(dp[i][j]) dp[i][j]+=j;
        }
    }
    for(int i=0;i<203;i++){
        Max = std::max(Max,dp[n-1][i]);
    }
    if(Max==0){
        printf("YI DING YAO JIAN CHI JI HUA SHENG YU");
    }
    else{
        tot = Max;
        tot/=(n-1);
        printf("%.2f\n",tot);
    }
    return 0;
}
P390

 

P311 乘积最大

题目梗概:

给出一个长度为n的数字串,给出k个乘号。

计算出最大乘积

 

思考:

dp[i][j] 表示位置为i j个乘号

dp[i][j]=max(dp[l][j-1]*sum[l+1][i],dp[i][j])

算是一道经典题目了。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=50;
long long sum[maxn][maxn],f[maxn][maxn];
int n,k;
int main()
{
    int temp[maxn];
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%1d",&temp[i]);
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            sum[i][j]=sum[i][j-1]*10+temp[j];//预处理,将i到j位的数字转化为数
    for(int i=1;i<=n;i++)
    f[i][0]=sum[1][i];
    for(int l=1;l<=k;l++)//枚举每一个乘号
        for(int i=l+1;i<=n;i++)//枚举每一个数位
            for(int j=l;j<i;j++)//找断开的部分
            f[i][l]=max(f[i][l],f[j][l-1]*sum[j+1][i]);
    printf("%lld\n",f[n][k]);
    return 0;
}
P311

 

P329 刘翔!加油

 

 

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

 

P329 刘翔加油

题目梗概:

已经不想解释这种背包DP的题目了(给出链接自己看吧),都是一个套路。

 

思考:

二维费用背包,只不过按照题目要求。需要题目提前排序,所以需要结构体。

#include <cstdio>
#include <algorithm>

int n,m,t;
struct node{
    int value,time,h,w;
    int num;
}now[105];
int dp[105][105];

bool CMP(const node &a,const node &b){
    return a.value<b.value;
}

int main(){
    scanf("%d%d%d",&n,&m,&t);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d",&now[i].value,&now[i].time,&now[i].h,&now[i].w);
        now[i].num=i;
    }
    std::sort(now+1,now+1+n,CMP);
    for(int i=1;i<=n;i++){
        for(int j=m;j-now[i].h>=1;j--){
            for(int k=t;k-now[i].time>=0;k--){
                dp[j][k] = std::max(dp[j-now[i].h][k-now[i].time]+now[i].w,dp[j][k]);
            }
        }
    }
    printf("%d\n",dp[m][t]);
}
P329

 

P429 词链

题目梗概:

给出一些单词,从给定单词中选出某个单词是另一个单词前缀的情况并统计。求最大的数量。

 

思考:

trie树维护,差不多是模板题目。

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define maxn 51*100000
struct list{
  int id,num;
  struct list *next[27];
}*root;
int ans;
struct list *build(){
  int i;
  struct list *p;
  p=new list;
  p->id=0;
  p->num=0;
  for(i=0;i<27;i++) p->next[i]=0;
  return p;
}
void query(char str[]){
  int mn;
  mn=0;
  struct list *p;
  p=root;
  int n,j;
  n=strlen(str);
  for(j=0;j<n;j++){
    int c=str[j]-'a';
    if(p->next[c]==NULL) p->next[c]=build();
    p=p->next[c];
    mn=max(mn,p->num);
  }
  p->num=mn+1;
  ans=max(ans,mn+1);
}
int main(){
  int n,i;
  char str[101];
  scanf("%d",&n);
  ans=0;
  root=build();
  for(i=0;i<n;i++){
    scanf("%s",str);
    query(str);
  }
  printf("%d\n",ans);
}
P429

 

 

P329 炮兵阵地

题目梗概:

这个是状压DP入门经典题目,发一个有图的链接自己看吧。

 

思考:

对于每行如果放置则为1,不放置则为0.

对于每行的情况来说,连续的炮兵必须间隔2格以上。所以得预处理一下。并统计一下这种情况的兵数量

先预处理出第一行的情况。

之后dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][m]+num[i]) 表示第i行 当前状态为j 上一行状态为k 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105;
int Map[N];
int dp[N][65][65]; 

int s[N], num[N];
int n, m, p;

bool check(int x) { 
    if(x & (x >> 1)) return false;
    if(x & (x >> 2)) return false;
    return true;
}

int Count(int x) { 
    int i = 1, ans = 0;
    while(i <= x) {
        if(x & i) ans++;
        i <<= 1;
    }
    return ans;
}

void Init() {
    p = 0;
    memset(s, 0, sizeof(s));
    memset(num, 0, sizeof(num));
    for(int i = 0; i < (1 << m); i++) { 
        if(check(i)) {
            s[p] = i;
            num[p++] = Count(i);
        }
    }
}

int main() {
    char ch;
        scanf("%d%d", &n, &m);
        if(!n && !m) {
            printf("0\n");
            return 0;
        }
        memset(dp, 0, sizeof(dp));
        memset(Map, 0, sizeof(Map));
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                cin >> ch;
                if(ch == 'H')
                    Map[i] = Map[i] | (1 << (m - 1 - j));  //P为0,H为1
            }
        }
        Init();
        for(int i = 0; i < p; i++) { 
        //printf("num[i]:%d  s[i]:%d\n",num[i],s[i]);
            if(!(Map[0] & s[i]))
                dp[0][i][0] = num[i];
        }
        for(int i = 0; i < p; i++) {  
            if(!(Map[1] & s[i])) {
                for(int j = 0; j < p; j++) {
                    if((!(s[i] & s[j]))) {
                        dp[1][i][j] = max(dp[1][i][j], dp[0][j][0] + num[i]);
                    }
                }
            }
        }
        for(int r = 2; r < n; r++) {  
            for(int i = 0; i < p; i++) { 
                if(!(s[i] & Map[r])) {
                    for(int j = 0; j < p; j++) {  
                        if(!(s[j] & Map[r-1])) {
                            if(!(s[i] & s[j])) {
                                for(int k = 0; k < p; k++) {  
                                    if(!(s[k] & Map[r-2])) {
                                        if(!(s[j] & s[k])) {
                                            if(!(s[i] & s[k])) {
                                                dp[r][i][j] = max(dp[r][i][j], dp[r-1][j][k] + num[i]);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        int ans = 0;
        for(int i = 0; i < p; i++) {
            for(int j = 0; j < p; j++) {
                if(ans < dp[n-1][i][j])
                    ans = dp[n-1][i][j];
            }
        }
        printf("%d\n", ans);
    return 0;
}
P328

 

 

 

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

 

转载于:https://www.cnblogs.com/OIerLYF/p/7460178.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值