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