Kuangbin带你飞 基础DP专题

bin神的DP专题真的有好多啊,整理一下做过的基础DP专题。

HDU1029 Ignatius and the Princess IV

http://acm.hdu.edu.cn/showproblem.php?pid=1029

题意

给出n,和n个数,找出至少出现(n+1)/2次的数字。

思路

至少出现(n+1)/2次,如果这个数存在那么排序后位于中间位置的一定是这个特殊的数。

HDU1069 Monkey and Banana

http://acm.hdu.edu.cn/showproblem.php?pid=1069

题意

一个猴子要通过堆箱子的方式拿到挂在高出的香蕉。现在给出n个类型的箱子,和这n个箱子的长宽高,一种类型的箱子可以有无数个,要求放在上一个的箱子的长宽,必须严格小于放在下面的箱子。比如说有一个10 20 30的箱子,我们可以将以10 20 为底30 为高的箱子放在以20 30 为底10 为高的箱子上。要求输出堆箱子能够到达的最高高度。

思路

写这道题之前先了解一下有向无环图中任意起点的最长路。dp[i]代表从i点出发能够到达的最长路径长度,很容易想到,dp[i]=max(dp[j]+maps[i][j],dp[i]),j点是i点的下一节点。实现代码如下:

int DP(int m,int n){
    if(dp[m]>0)return dp[m];
    for(int i=1;i<=n;i++){
        if(maps[m][i]!=INF){
            dp[m]=max(dp[m],DP(i,n)+maps[m][i]);
        }
    }
    return dp[m];
}

经典例题矩形嵌套问题,就是利用最长路解决的。矩形嵌套问题就是给若干个矩形的长宽,找出能够最多嵌套的矩形个数。将每个矩形看作有向图上的点,能够嵌套的两个矩形则在两点之间形成有向边,边权为1,求出有向图的任意起点的最长边,那么就找出最大值。Monkey and Banana 这道题就是矩形嵌套的变形,将一个立方体分解成带有点权的矩形,再利用矩形嵌套找到点权能达到最大的路径。代码如下:

HDU 1084 What Is Your Grade

http://acm.hdu.edu.cn/showproblem.php?pid=1084

题意

现在有五道题,如果你全部做完可以得到满分,如果一道题都没做完,只能得到50分,如果你做了4道,你可能得到95分,也可能得到90分,取决于你是否在做完4道题的所有学生中是否是最早做完的那一半学生。是则获得95分,否则获得90分。以此类推。

思路

沙雕题,不多说了。。

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

using namespace std;
const int MAXN=110;
struct students{
    int num;
    int hour,minn,sec;
};
students stu[MAXN];
int score[]={50,60,70,80,90,100};
int main(){
    int n;
    while(cin>>n&&n!=-1){
        for(int i=1;i<=n;i++){
            scanf("%d %d:%d:%d",&stu[i].num,&stu[i].hour,&stu[i].minn,&stu[i].sec);
        }
        for(int i=1;i<=n;i++){
            int ans=0,sum=1,itemp=stu[i].hour*3600+stu[i].minn*60+stu[i].sec;
            if(stu[i].num==5||stu[i].num==0){
                cout<<score[stu[i].num]<<endl;
                continue;
            }
            for(int j=1;j<=n;j++){
                if(i==j)continue;
                int jtemp=stu[j].hour*3600+stu[j].minn*60+stu[j].sec;
                if(stu[i].num==stu[j].num){
                        if(jtemp<itemp){
                            ans++;
                        }
                        sum++;
                }
            }
                if(ans>=sum/2){
                    cout<<score[stu[i].num]<<endl;
                }
                else cout<<score[stu[i].num]+5<<endl;
        }
        cout<<endl;
    }
    return 0;
}

HDU1114 Piggy-Bank

http://acm.hdu.edu.cn/showproblem.php?pid=1114

题意

给一个猪猪存钱罐为空的时候的重量,存钱罐装有一定硬币的重量。再给定几种类型的硬币的价值和重量,硬币可以无限取,最终要使得存钱罐达到指定的重量,且硬币价值和最小。

思路

一个非常裸的完全背包,只是一般找价值大的,这次找价值小的。。。。判断是否能够有取的方案达到指定重量,只需判断是否等于初始化值。

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

using namespace std;
const int MAXN=1e5+100;
typedef long long ll;
const int INF=9999999;
int v[MAXN];
int w[MAXN];
ll dp1[MAXN],dp2[MAXN];
int main(){
    int t;
    cin>>t;
    while(t--){
        int n,pig,weight;
        cin>>pig>>weight>>n;
        for(int i=1;i<=n;i++){
            cin>>v[i]>>w[i];
        }
        for(int i=0;i<MAXN;i++){
            dp1[i]=INF;dp2[i]=INF;
        }
        for(int i=1;i<=n;i++){
            dp1[0]=0;
            for(int j=1;j<w[i];j++){
                dp1[j]=dp2[j];
            }
            for(int j=w[i];j<=weight-pig;j++){
                dp1[j]=min(dp2[j],dp1[j-w[i]]+v[i]);
            }
            for(int j=1;j<=weight-pig;j++){
                dp2[j]=dp1[j];
            }
        }
        if(dp1[weight-pig]!=INF)cout<<"The minimum amount of money in the piggy-bank is "<<dp1[weight-pig]<<"."<<endl;
        else cout<<"This is impossible."<<endl;
    }
    return 0;
}

HDU1176 免费馅饼

思路

dp[j][i],代表第j个位置第i秒时获得馅饼的数量。状态转移方程是dp[j][i]=max(dp[j+1][i+1],dp[j-1][i+1],dp[j][i+1]),即对于当前位置当前时间,肯定是由左右和当前位置的后一秒的最优解转移过来的。

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


using namespace std;
const int MAXN=1e5+10;
long long dp[15][MAXN];
struct nodes{
    int x,t;
};
nodes num[MAXN];
int main(){
    int n;
    while(scanf("%d",&n)!=EOF&&n){
        int maxtime=-1;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            scanf("%d%d",&num[i].x,&num[i].t);
            dp[num[i].x][num[i].t]++;
            maxtime=max(maxtime,num[i].t);
        }
        for(int i=maxtime-1;i>=0;i--){
            for(int j=0;j<=10;j++){
                long long temp;
                if(j-1>=0&&j+1<=10){
                    temp=max(max(dp[j-1][i+1],dp[j+1][i+1]),dp[j][i+1]);
                }
                else if(j==0){
                    temp=max(dp[j][i+1],dp[j+1][i+1]);
                }
                else if(j==10){
                    temp=max(dp[j][i+1],dp[j-1][i+1]);
                }
                dp[j][i]+=temp;
            }
        }
        printf("%lld\n",dp[5][0]);
    }
    return 0;
}

HDU1260 Tickets

http://acm.hdu.edu.cn/showproblem.php?pid=1260

题意

一个只想摸鱼的程序猿售票员,他想尽可能早地回家。他能够一张一张地卖票,也可以相邻两张两张地卖票,给出所需要消耗的时间,求出售票员能够最早回家的时间。

思路

简单的dp,卖到第i张票所需的时间,取  卖到第i-1张票所需时间+第i张单票所需时间 ,卖到第i-2张票所需时间+第i-1和i张连票所需时间  的最大值。

#include  <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iomanip>

using namespace std;
const int MAXN=2*1e3+100;
int num[MAXN];
int adj[MAXN];
int dp[MAXN];
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&num[i]);
        }
        for(int i=2;i<=n;i++){
            scanf("%d",&adj[i]);
        }
        dp[1]=num[1];
        dp[2]=min(dp[1]+num[2],adj[2]);
        for(int i=3;i<=n;i++){
            dp[i]=min(dp[i-1]+num[i],dp[i-2]+adj[i]);
        }
        int hour=8,minn=0,sec=0;
        hour+=dp[n]/3600;
        dp[n]=dp[n]%3600;
        minn+=dp[n]/60;
        dp[n]=dp[n]%60;
        sec+=dp[n];
        if(hour>12)
            cout<<setw(2)<<setfill('0')<<hour-12<<":"<<setw(2)<<setfill('0')<<minn<<":"<<setw(2)<<setfill('0')<<sec<<" pm"<<endl;
        else
            cout<<setw(2)<<setfill('0')<<hour<<":"<<setw(2)<<setfill('0')<<minn<<":"<<setw(2)<<setfill('0')<<sec<<" am"<<endl;
    }
    return 0;
}

HDU1257 最少拦截系统

http://acm.hdu.edu.cn/showproblem.php?pid=1257

思路

贪心一下,拿第一个把能拦截的都拦截了,再重复这一过程。

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

using namespace std;
const int MAXN=1e6+100;
int num[MAXN];
bool used[MAXN];
int main(){
    int n;
    while(cin>>n){
    memset(used,false,sizeof(used));
    for(int i=1;i<=n;i++){
        scanf("%d",&num[i]);
    }
    int temp=-1,ans=0;
    for(int i=1;i<=n;i++){
        if(used[i])continue;
        temp=num[i];
        used[i]=true;
        ans++;
        for(int j=i+1;j<=n;j++){
            if(num[j]<=temp&&!used[j]){
                temp=num[j];
                used[j]=true;
            }
        }
    }
    cout<<ans<<endl;
    }
    return 0;
}

HDU1160 FatMouse's Speed

题解

n个老鼠的体重和速度,找出一个子序列,使得体重严格递增,速度严格递减。找到最长的这样的序列。

思路

将数据按体重排序之后,找最长下降子序列,并记录路径。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <stack>

using namespace std;
struct mice{
    int w,s;
    int order;
    bool operator <(const mice a)const{
        if(w!=a.w)
        return w<a.w;
        else return a.s<s;
    }
};
const int MAXN=1e3+100;
mice mouse[MAXN];
int dp[MAXN];
int pre[MAXN];
int main(){
    int n,m;
    int k=1;
    while(scanf("%d%d",&n,&m)!=EOF){
        mouse[k].w=n;
        mouse[k].s=m;
        mouse[k].order=k;
        k++;
    }
    for(int i=0;i<MAXN;i++){
        pre[i]=-1;
    }
    memset(dp,0,sizeof(dp));
    sort(mouse,mouse+k);
    dp[1]=1;
    int minnum=1,temp=1;
    for(int i=2;i<k;i++){
        bool flag=false;
        for(int j=i-1;j>=1;j--){
            if(mouse[j].w<mouse[i].w&&mouse[j].s>mouse[i].s){
                flag=true;
                if(dp[j]+1>dp[i]){
                    dp[i]=dp[j]+1;
                    pre[i]=j;
                }
            }
        }
        if(!flag)dp[i]=1;
        if(minnum<dp[i]){minnum=dp[i];temp=i;}
    }
    cout<<minnum<<endl;
    stack<int>s;
    for(int i=1;i<=minnum;i++){
        s.push(mouse[temp].order);
        temp=pre[temp];
    }
    while(!s.empty()){
        cout<<s.top()<<endl;
        s.pop();
    }
    return 0;
}

TO BE CONTINUE

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
并查集是一种常用的数据结构,用于管理一个不相交集合的数据。在并查集中,每个元素都有一个父节点指向它所属的集合的代表元素。通过查找和合并操作,可以判断两个元素是否属于同一个集合,并将它们合并到同一个集合中。 在解决某些问题时,可以使用并查集进行优化。例如,在区间查询中,可以通过优化并查集的查询过程,快速找到第一个符合条件的点。 对于拆边(destroy)操作,一般的并查集无法直接实现这个功能。但是可以通过一个巧妙的方法来解决。首先,记录下所有不会被拆除的边,然后按照逆序处理这些指令。遇到拆边操作时,将该边重新加入并查集中即可。 在实现并查集时,虽然它是一种树形结构,但只需要使用数组就可以实现。可以通过将每个元素的父节点记录在数组中,来表示元素之间的关系。通过路径压缩和按秩合并等优化策略,可以提高并查集的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [「kuangbin专题五并查集专题题解](https://blog.csdn.net/weixin_51216553/article/details/121643742)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [并查集(详细解释+完整C语言代码)](https://blog.csdn.net/weixin_54186646/article/details/124477838)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值