区间DP

啊~~

我来了,

蒟蒻!!!

十分无厘头!

今天我们来介绍一下线性dp的进阶区间dp

对于区间dp来说,是一类题型,也是dp的重要考点、

对于动态规划,我们知道“阶段”是最重要的,那区间dp的状态就是区间长度。

它常常来解决一些区间问题,由于一些区间太大,所以我们将大区间化为小区间,然后将小区间进行动态规划,最后再一步步合成所求区间。

这就是区间dp的基本思路。当然我们一会儿将会提到记忆化搜索与区间dp的结合应用。

首先,我们来看一道基础区间dp

石子合并(线性版):

题目描述

设有N堆沙子排成一排,其编号为1,2,3,…,N(N<=300)。每堆沙子有一定的数量,可以用一个整数来描述,现在要将这N堆沙子合并成为一堆,每次只能合并相邻的两堆,合并的代价为这两堆沙子的数量之和,合并后与这两堆沙子相邻的沙子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同,如有4堆沙子分别为 1  3  5  2 我们可以先合并1、2堆,代价为4,得到4 5 2 又合并 1,2堆,代价为9,得到9 2 ,再合并得到11,总代价为4+9+11=24,如果第二步是先合并2,3堆,则代价为7,得到4 7,最后一次合并代价为11,总代价为4+7+11=22;问题是:找出一种合理的方法,使总的代价最小。输出最小代价。

输入格式

第一行一个数N表示沙子的堆数N。
第二行N个数,表示每堆沙子的质量(<=1000)。

输出格式

合并的最小代价

样例输入
4
1 3 5 2
样例输出
22

看完题,我们一种暴力的想法就是一个一个枚举,当然这样我们会理所当然的TLE(傻子都知道好吗!!

这时,我们可以换个思路,我们假设两堆石子lr,当两堆石子可以被合并时,代表l到r中间的所有石子都已经被合并了,只有这样才能时l和r两堆石子相邻。

且其中的石子数量是ri=lAi,所以,我们可以知道一定存在一个k(l≤k<r),可以使l到r中间的所有石子合并起来。即:

在[l,k]的石子与[k+1,r]之间的石子合并,并最终得到[l,r]之间的石子。

所以,我们就可以推出状态转移方程:

 

1 for(int len=1;len<=n;len++){//表示阶段,表示每一次最多可以有几堆被合并 
2         for(int l=1;l<=n-len+1;l++){//表示状态,左端点 
3             int r=l+len-1;//表示右端点
4             for(int k=l;k<r;k++){//决策点,又名中途转折点
5                 f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+sum[r]-sum[l-1]);//状态转移方程 
6             } 

 

 

 

这样我们就解决这个问题,最后处理一下答案然后输出即可。

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define maxn 307
 4 int f[maxn][maxn],n,sum[maxn],a[maxn];
 5 //template<typename __Type_of_scan>
 6 //void scan(__Type_of_scan &x){
 7 //    __Type_of_scan f=1;x=0;char s=getchar();
 8 //    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
 9 //    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
10 //    x*=f;
11 //}
12 int main(){
13 //    scan(n);
14     scanf("%d",&n);
15 //    memset(sum,0,sizeof(sum));
16     memset(f,0x3f,sizeof(f));//初始化 
17     for(int i=1;i<=n;i++){
18 //        scan(a[i]);
19         scanf("%d",&a[i]);
20         f[i][i]=0;//进行清零 
21         sum[i]=sum[i-1]+a[i];//求前缀和 
22     }
23 //    for(int i=1;i<=n;i++){
24 //        f[i][i]=0;
25 //        sum[i]=sum[i-1]+a[i];
26 //    }
27     for(int len=1;len<=n;len++){//表示阶段,表示每一次最多可以有几堆被合并 
28         for(int l=1;l<=n-len+1;l++){//表示状态,左端点 
29             int r=l+len-1;//表示右端点
30             for(int k=l;k<r;k++){//决策点,又名中途转折点
31                 f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+sum[r]-sum[l-1]);//状态转移方程 
32             } 
33 //            f[l][r]+=sum[r]-sum[l-1];//更新每个点的权值 
34         }
35     }
36 //    int minn=0x3f;
37 //    int maxx=0;
38 //    for(int i=1;i<=n;i++){
39 //        minn=min(minn,f[i][n-i+1]);
40 //        maxx=max(maxx,f[i][n-i+1]);
41 //    } 
42 //    printf("%d\n",minn);
43 //    printf("%d\n",maxx);
44     printf("%d",f[1][n]);
45     return 0;
46 }
石子合并(非环)

 

 

 

下面,我们来看一看加大难度的石子合并

环形石子合并

题目描述:

 

题目描述

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入输出格式

输入格式:

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式:

输出共2行,第1行为最小得分,第2行为最大得分.

输入输出样例

输入样例#1:
4
4 5 9 4
输出样例#1:
43
54

 

提示一下:把环变成链即可

代码:

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define maxn 307
 4 int f1[maxn][maxn],n,sum[maxn],a[maxn],f2[maxn][maxn];
 5 //template<typename __Type_of_scan>
 6 //void scan(__Type_of_scan &x){
 7 //    __Type_of_scan f=1;x=0;char s=getchar();
 8 //    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
 9 //    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
10 //    x*=f;
11 //}
12 int main(){
13 //    scan(n);
14     scanf("%d",&n);
15 //    memset(sum,0,sizeof(sum));
16 //    memset(f2,0x3f,sizeof(f2));//最大值数组初始化 
17     for(int i=1;i<=n;i++){
18 //        scan(a[i]);
19         scanf("%d",&a[i]);
20         a[i+n]=a[i];//因为是环,所以要存两倍数 
21 //        f2[i][i]=0;
22 //        f1[i][i]=0;//进行清零 
23 //        sum[i]=sum[i-1]+a[i];//求前缀和 
24     }
25     for(int i=1;i<=n*2;i++){
26         f1[i][i]=0;
27         f2[i][i]=0;
28         sum[i]=sum[i-1]+a[i];}//求前缀和 
29     for(int len=2;len<=n;len++){//表示阶段,表示每一次最多可以有几堆被合并 
30         for(int l=1;l<=2*n-len;l++){//表示状态,左端点 
31             int r=l+len-1;//表示右端点
32             f2[l][r]=0x3f3f3f3f;//每一次p跑最小值时都要把这个点f2初始为最大
33             for(int k=l;k<r;k++){//决策点,又名中途转折点,区间断点 
34                 f1[l][r]=max(f1[l][r],f1[l][k]+f1[k+1][r]);//f1表示最大值状态转移方程 
35                 f2[l][r]=min(f2[l][r],f2[l][k]+f2[k+1][r]);//f2表示最小值状态转移方程 
36             } 
37             f1[l][r]+=(sum[r]-sum[l-1]);//更新每个点的权值 
38             f2[l][r]+=(sum[r]-sum[l-1]);
39         }
40     }
41 //    for(int l=2;l<=n;l++){
42 //        for(int p=1;p<=2*n-l;p++){
43 //            int j=p+l-1;
44 //            f1[p][j]=0;                //f1表示最大值,f2表示最小值 
45 //            f2[p][j]=1926817;      //初始化最大最小值 
46 //        for(int k=p;k<j;k++){   //k表示区间断点 
47 //            f1[p][j]=max(f1[p][j],f1[p][k]+f1[k+1][j]);   // 
48 //            f2[p][j]=min(f2[p][j],f2[p][k]+f2[k+1][j]);   // 
49 //        } 
50 //        f1[p][j]+=(sum[j]-sum[p-1]);
51 //        f2[p][j]+=(sum[j]-sum[p-1]);
52 //        }
53 //    }
54 //    int minn=0x3f;
55 //    int maxx=0;
56 //    for(int i=1;i<=n;i++){
57 //        minn=min(minn,f1[i][n-i+1]);
58 //        maxx=max(maxx,f2[i][n-i+1]);
59 //    } 
60     int dp1=0;
61     int dp2=0x3f3f3f3f;
62     for(int i=1;i<=n;i++){
63         dp1=max(dp1,f1[i][i+n-1]);
64         dp2=min(dp2,f2[i][i+n-1]);
65     }
66     printf("%d\n",dp2);
67     printf("%d\n",dp1);
68 //    printf("%d",f[1][n]);
69     return 0;
70 }
石子合并(环形)

 

 于是,看完石子合并这个十分基础的题,我们来看一看一道poj1179

这也是一道十分经典的区间dp的题,大家可以做一做,给点提示:把环变链再缩短,最后出答案(注意最大最小值的更新)。

这道题的→_→  题解~~

在给大家推荐一道题,是一道《算法进阶指南》的题,金字塔

区间dp题目推荐(持续更新)

洛谷P1063(能量项链)

洛谷P1220(多维区间dp)

 

 

 

 

 
 
 
 
 
 
 
 
 
 
 
 

转载于:https://www.cnblogs.com/xishirujin/p/10432858.html

标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广泛使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模型构建、验证与优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模型 逻辑回归是一种常用于分类任务的线性模型,特别适用于二类问题。在信用评估中,该模型可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模型 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约与非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理与转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模型的适应性。 六、模型构建与验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模型性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模型的召回率与特异性。 七、集成学习方法 为提升模型预测能力,可采用集成策略,如结合多个模型的预测结果。这有助于降低单一模型的偏差与方差,增强整体预测的稳定性与准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理与模型优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模型以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值