7.21 暑假集训——动态规划篇(二)

3 篇文章 0 订阅
2 篇文章 0 订阅
本文详细记录了17道动态规划题目,包括E-Coin、B-免费馅饼等,涉及经典裸题和复杂题目如树形DP、区间DP等。通过题解,读者可以深入理解动态规划的多种应用和解题技巧。
摘要由CSDN通过智能技术生成

共17道题目,其中最后五题是拓展题,数位dp和插头dp没有写出来
按照过题的顺序记录, 可以从下往上看~

E-Coins

题意: 告诉你 n 种钱币的面值和数目,求【1,m】之间,有多少数目是用这些钱币能恰好凑出的?
思路:经典裸题,参考《挑战程序设计》P74

注意: 内存限制,可以参考书上的利用奇偶性减少内存消耗

B-免费馅饼

题意: 现在给这条小径如图标上坐标:
————————————
0 1 2 3 4 5 6 7 8 9 10
假设馅饼都掉落在0-10这11个位置。gameboy每秒种只有在移动不超过一米的范围内接住坠落的馅饼。开始时gameboy站在5这个位置,因此在第一秒,他只能接到4,5,6这三个位置中其中一个位置上的馅饼。问gameboy最多可能接到多少个馅饼?(假设他的背包可以容纳无穷多个馅饼)
思路: 定义以时间为序,决策有 站着不动 ,左移和右移两种,开一个数组记录在t时刻,i点处掉落的馅饼数

J-Humble Numbers

题意: 只由2,3,5,7相乘得到的数字为Humble Numbers,如1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 25, 27, …
求第n个Humber Numbers
思路: 参考紫皮 第十章
注意: G++和C++ 编译上的不同

D-How to type

思路: 定义状态d[i][0]和d[i][1]分别表示第i个字符大写和小写所需的最小操作数,递推一下

A- Big event in HDU ****

思路: 要转换一下思路,转换成求0-sum 值中用有n人的m个学院最多凑出多少个数,然后找出这些数中最靠近sum/2的数即可
code:http://paste.ubuntu.com/25197810/

L-How many ways

题意: 这是一个简单的生存游戏,你控制一个机器人从一个棋盘的起始点(1,1)走到棋盘的终点(n,m)。游戏的规则描述如下:
1.机器人一开始在棋盘的起始点并有起始点所标有的能量。
2.机器人只能向右或者向下走,并且每走一步消耗一单位能量。
3.机器人不能在原地停留。
4.当机器人选择了一条可行路径后,当他走到这条路径的终点时,他将只有终点所标记的能量。
我们的问题是机器人有多少种方式从起点走到终点。这可能是一个很大的数,输出的结果对10000取模。
code:http://paste.ubuntu.com/25197837/

Beans ****

题意 这里写图片描述
注意:
分别开一个行和列的数组,注意最优解可以隔一行,或者隔两行取
code:http://paste.ubuntu.com/25197890/

Doint homework again

思路: 转换一下思路,求怎样安排获得的分最大,最后减一下最大值就可以了,应该也算01背包?
code: http://paste.ubuntu.com/25197962/

树形dp ****

题意 公司里上级和下级形成一棵树,每个人期待参加派对的程度都不同,求在直接上级和下级不同时出现在派对的情况下,最大的期待总值
思路: 先是怎么储存树,我用的vector
然后分别用0,1存储这个人不参加和参加派对时的最大期待值
code: http://paste.ubuntu.com/25198008/

G&H 求最大相同字符的子矩阵面积 *****

思路: 本来是很不会矩阵求面积的…… 后来居然看着看着 看通畅了……
记录每一列的最大深度,求出到最左和最右的距离,用这个面积来更新ans
code: http://paste.ubuntu.com/25198040/

因为最后两题想直接给出代码,所以把两题没有做出来的放到上面来

数位dp

题意: 求一个区间内满足条件的数有多少个,条件是该数可以写成k个b的不同次方的和
参考: 刘聪 《浅谈数位类统计问题》

插头dp HDU P1693

题意: 这里写图片描述

呃…… 略略略

状压dp *********

思路: 先看数据范围 1 ≤ M ≤ 12; 1 ≤ N ≤ 12 可以状压没有错
然后枚举,d[i][j] 为第i行为j排列时 最大的排列数

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
int pic[15][15];
const int bit=(1<<14)-1;
LL d[15][bit+10];
int n,m;
const int mod=1e8;

bool able(int i,int j){ //判断第i行 j种排列可行,1.没有两个1相邻,2.没有荒地种了1
    int lim=0;
    for(int k=0;k<m;k++)
        lim=(lim<<1)+pic[i][k];
    if(lim&j) return false;
    int be=j&1;j>>=1;
    while(j){
        if(be==1&&(j&1)) return false;
        be=j&1; j>>=1;
    }
    return true;
}

bool judge(int i,int j){  //判断相邻两行的i,j排列是否可行,相邻字符不都为1则可行
    bool res=true;
    for(int k=0;k<m;k++){
        int n=i&1,m=j&1;
        if(n==1&&m==1) res=false;
        i>>=1,j>>=1;
    }
    return res;
}

void print(int j){ //输出解
    while(j){
        printf("%d",j&1);
        j>>=1;
    }
    cout<<endl;
}


int main()
{
    //freopen("1.txt","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        memset(pic,0,sizeof(pic));
        memset(d,0,sizeof(d));
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++){
            int t;
            scanf("%d",&t);
            pic[i][j]=!t;
        }

        for(int i=0;i<(1<<m);i++)
                if(able(0,i)) d[0][i]=1;

        for(int i=1;i<n;i++){
            for(int j=0;j<(1<<m);j++){
                if(!able(i,j)) {d[i][j]=0;continue;}  //第i行可以取j排列
                for(int k=0;k<(1<<m);k++){
                    if(judge(j,k)&&able(i-1,k)) d[i][j]=(d[i][j]+d[i-1][k])%mod;  //i-1行可以取k排列,i行以取j排列时 可以取的排列数
                }
            }
        }

        LL ans=0;
        for(int j=0;j<(1<<m);j++) ans=(ans+d[n-1][j])%mod;
        printf("%lld\n",ans%mod);
    }
    return 0;
}

区间dp **********

思路: 给出一个多边形,已知每一个顶点的坐标,告诉你每一刀的消耗与坐标的关系式 costi, j = |xi + xj| * |yi + yj| % p 。求将这个多边形分为n-2个三角形所需要的最小消耗

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
struct Point{  //定义点的结构体
    int x,y;
    Point(int x=0,int y=0):x(x),y(y) {};
};
bool operator < (const Point& a,const Point& b){ //两种排序
    return (a.x!=b.x) ? a.x<b.x: a.y>b.y;
}
bool cmp (const Point &a,const Point &b){
    return (a.x!=b.x) ? a.x>b.x:a.y<b.y;
}
vector<Point> vec;  //点的向量集
const int INF=1e9;
int n,q;
int d[310][310];
int c[310][310];
//已知三角形三个顶点的坐标,求面积,利用公式
double area(int i,int j,int k){
    Point a=vec[i],b=vec[j],c=vec[k];
    return (fabs(a.x*(b.y-c.y)+b.x*(c.y-a.y)+c.x*(a.y-b.y))/2.0);
}
//判断点是不是在已有的多边形的内部,如果是则为凹多变形,理解
bool judge(int i,int j,int k){
    for(int t=0;t<n;t++){
        if(t==i||t==j||t==k) continue;
        if(area(i,j,k)==area(i,j,t)+area(i,k,t)+area(j,k,t)) return false;
    }
    return true;
}

// 用动态规划求最优解,重点d[i][j]=min(d[i][j],d[i][k]+d[k][j]+c[i][k]+c[k][j]);
bool solve(){
    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++){
        if(i-j==1||j-i==1) c[i][j]=c[j][i]=0;
        else {
            c[i][j]=c[j][i]=(abs(vec[i].x+vec[j].x)*abs(vec[i].y+vec[j].y))%q;
        }
    }

    for(int i=n-2;i>=0;i--)
        for(int j=i+1;j<n;j++){
            if(j==i+1) d[i][j]=0;
            else{
                d[i][j]=INF;
                for(int k=i+1;k<j;k++){
                    if(!judge(i,j,k)) return false;  //判断凸边形
                    d[i][j]=min(d[i][j],d[i][k]+d[k][j]+c[i][k]+c[k][j]);
                }
            }
    }

    return  true;
}
//给点排序,使点按逆时针方向形成一个多边形
void Sort(){
    sort(vec.begin(),vec.end());
    int lx,ly,rx,ry;
    lx=vec[0].x,ly=vec[0].y;
    rx=vec[n-1].x,ry=vec[n-1].y;
    double k=double (ly-ry)/double (lx-rx);
    double b=double(ly)-k*lx;
    vector<Point> vec2;
    vec2.clear();
    for(int i=0;i<n;i++)
    {
        if(vec[i].x*k-vec[i].y+b<=0){
            vec2.push_back(vec[i]);
            vec[i].x=vec[i].y=-INF;
         }
    }
    sort(vec.begin(),vec.end(),cmp);
    for(int i=0;i<n;i++)
        if(vec[i].x>=lx) vec2.push_back(vec[i]);
    swap(vec,vec2);
}


int main()
{
    //freopen("1.txt","r",stdin);
    while(~scanf("%d%d",&n,&q)){
        vec.clear();
        for(int i=0;i<n;i++){
            int x,y; scanf("%d%d",&x,&y); //读取每个点
            vec.push_back(Point(x,y));
        }
        Sort();
        memset(d,-1,sizeof(d));

        bool jud=solve();
        if(!jud){
            printf("I can't cut.\n"); continue;
        }
        printf("%d\n",d[0][n-1]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值