ACM第四五周dp总结

解题总结

总结:
近期做的题,思考量很大,但不管怎样最有效的途径都是一点一点思考去解决。C题老鼠花了很长时间一开始做只想套方程模板,调试的过程中越想AC,可能耗费的时间会越长因为根本找不到自己的问题,所以仔细分析慢就等于快。接下来,再来仔细回顾一下做题过程,老鼠体重越大,速度越小。输入多组老鼠weight and v,输出体重严格上升,速度严格下降的最长序列长度和位置。又是一个序列性问题,但有点难道的是两个序列,两个序列想到最长公共子序列,最大子矩阵和,最大m字段和等但在他们之间没有找到可以联系的地方,这是用两个序列去思考。但贪心做过类似的题,题目同样有两个顺序要求,先固定一个简化问题,在这个题中同样适用,于是就转化成求最长上升子序列。还需要依据求最长上升子序列的动态方程来找到。C题我是根据将体重从小到大排序,找到速度的最长上升子序列,之后让输出了状态方程以及数据,来确定找下标的办法,寻找最长子序列下标时要以最长子序列长度和当前最大速度,因为是倒着遍历所以找之前较大的数,通过定义一个较小数不断被大数替换来实现。找到最长子序列的位置记录它们原来的下标,最后输出。这是刚做的一道题,具体实践还存在问题,这道题学到的新知识在于学会找到序列★,看了别人题解知道需要根据自己排序之后的数据加上条件限制输出。
最长上升子序列:

for( i = 2; i <= N; i ++ )
{
int nTmp = 0;
//记录第i个数左边子序列最大长度
for(j = 1; j < i; j ++ )
{
if( b[i] > b[j])
{
if(nTmp < aMaxLen[j])
nTmp = aMaxLen[j];
//找到以第i个数左边数为终点的最长上升子序列长度
}
}
aMaxLen[i] = nTmp + 1; //加上它本身,之后序列的长度
}
int nMax = -1;
for( i = 1; i <= N; i ++ )
if(nMax<aMaxLen[i])
nMax=aMaxLen[i];
cout <<nMax;

另一方式:
状态含义:F [ i ] 代表以 A [ i ] 结尾的 LIS 的长

状态转移:F [ i ] = max { F [ j ] + 1 ,F [ i ] } (1 <= j < i,A[ j ] < A[ i ])
初始值:F [ i ] = 1 (1 <= i <= n)

for(int i=1; i<=n; i++)
for(int j=1; j<i; j++)
if(a[j] < a[i])
f[i] = max(f[i], f[j]+1);
for(int i=1; i<=n; i++)
ans = max(ans, f[i]);
printf("%d\n", ans);

总结:
就是d[i]就是以a[i]结尾,在a[i]之前最长上升子序列+1,没有更小的话就是1,所以dp数组中最大长度就是最长子序列。需要通过循环找到最长的长度,与最长公共子序列不同,最长公共子序列动态数组最后的数就是最长的长度。接下来就来回顾一下最长公共子序列,dp作业Q题,Q题现在看来是最简单的最起初的了,只是让我们输出最大长度。而通过了解还会有输出最长公共子序列的要求,它又分为输出任意一个序列和全部输出。接下来是Q题的扩展

最长公共子序列:
DP[i][j]代表 A串从起点到i位置的字串 和 B串从起点到j位置的字串 的LCS
当A[i] == B[j] 时 :DP[i][j] = DP[i-1][j-1] + 1
当A[i] != B[j] 时 : DP[i][j] = MAX(DP[i-1][j] , DP[i][j-1])
code:

int LCS()
{
   dp[0][0]=0
   for(int i = 1 ; i < =n ; i++)
    for(int j = 1 ; j <= m ; j+++)
     	if(A[i] == B[j])
		dp[i][j] = dp[i-1][j-1]+1;
	else
	dp[i][j] = max(dp[i-1][j] , dp[i][j-1]);
   return dp[n][m];
}

总结一下:
确定一个决定性的序列,该代码决定性的i代表的序列,通过它来找到公共的序列,代码的理解要回到问题的本质,开始遍历,如果两个序列元素相同,那么让其各回到上一个位置,该位置序列长度加一,如果不同,就找出a序列与b序列的上一个和a序列上一个元素与b序列该元素的最长公共子序列的长度。那么最后的位置就是最长公共子序列的个数了,最最基础的。

Q题扩展:
要求:任意输出一个公共子序列。(需要用到递归的思想,还有就是标记)
样例输入
ABCBDAB
BDCABA
样例输出
4
BCBA
在这里插入图片描述
找来图片帮助理解。
个人理解:

int f[205][205],g[205][205];//g存储是那种状态转移来的,分别用-1,0,1来表示
void getans(int n,int m){
	if(!n||!m) return;//m或n为0结束
	if(g[n][m]==1){//选择这种情况输出进行输出
		getans(n-1,m-1);//递归到上一个状态
		printf("%c",a[n]);//会倒向输出
	}
	else if(g[n][m]==-1) getans(n-1,m);
	else getans(n,m-1);
}
//主函数
int lena=strlen(a+1),lenb=strlen(b+1);
	for(int i=1;i<=lena;++i){
		for(int j=1;j<=lenb;++j){//把max分开成两个if
	if(f[i-1][j]>f[i][j]) f[i][j]=f[i-1][j],g[i][j]=-1;//标记
	if(f[i][j-1]>f[i][j]) f[i][j]=f[i][j-1],g[i][j]=0;//标记
		if(a[i]==b[j]){
			if(f[i-1][j-1]+1>f[i][j]){
					g[i][j]=1;//标记
					f[i][j]=f[i-1][j-1]+1;
				}
			} 
		}
	}
	if(!f[lena][lenb]) printf("0");//无公共子序列
	else{
		printf("%d\n",f[lena][lenb]);
		getans(lena,lenb);//调用递归函数
	}

P题与以上的输出序列有些相似,只不过是最大字段和收尾子段位置的输出。与输出最长公共子序列的思想是类似的,都要将情况细分开来进行标记,位置的更替。但没有必要用到递归。因为子段本来就是连续的,而且还是一维。在第一次做的时候很难想清楚,因为想到当你从头开始遍历找最大和时,即使找到了最大和也不能确定之后有没有更大的。但其实是可以确定的,因为sum被当前的最大字段和替代,所以很显然之后就没有更大的啦,所有不用担心坐标再一次被更新。

int sum=0,num=1;
for(int i=1; i<=n; i++)
   { 
       sum+=a[i];
        if(maxx<sum)
       {   
         maxx=sum;
         k=num;//k会是一直不变的,直到开始一个新的子段
          m=i; 
          }
         if(sum<0)//重新开始,和归0,坐标移到下一位
        {
        sum=0;
         num=i+1;
         } }
        cout<<k<<m<<endl;

如果不需要输出首尾位置只找最大字段和的话,那么只需要一个状态转移方程和一个循环找最大值就OK。

int maxSubSum = 0;
dp[1] = Max(a[1], 0);//初始,取决于a(1)的±
int i;
for (i = 2; i <= N; ++i)
    dp[i] = Max(dp[i - 1] + a[i], 0);//状态方程
for (i = 1; i <= N; ++i) {//循环找最大
    if (maxSubSum < dp[i])
        maxSubSum = dp[i];
}

接下来整理了对我来说比较难理解的最大子矩阵和和最大m子段和的思想。最大子矩阵的思想:将二维转化一维,理解最大字段和的基础上,可以这样理解最大子矩阵和的问题。
举个栗子:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
(1)求矩阵大小是1★k//一行
可以发现就是求每行的最大连续子序和
0 -2 -7 0 (ans=0,矩阵为[0])
9 2 -6 2 (ans=11,矩阵为[9 2])
……
(2)求矩阵大小是2★k//两行
这时可以在第1,2行或2,3行或3,4行找最大矩阵,比如第一二行:
0 -2 -7 0
9 2 -6 2
最大矩阵是 0 9
因为是求和,不需要标记具体的位置,矩阵肯定是竖着一列都取的,不可能这一列取到第i个元素,上一列取到第i-1个元素。两行可以加起来
9 0 -13 2
这样求出的最大连续子序和是9,这个结果也就是这个矩阵对应的最大矩阵和。
(3)同理3★k和4★k,之后所有情况都清楚了。
通过i和j确定起始行和终止列,将其合并为一个一维序列,找最大子段和,这样就可以找到子矩阵了。实践的过程中来一个函数找出一行最大子段和,记得重置0。

void solve(int j){
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++){
        dp[i]=max(b[j][i],dp[i-1]+b[j][i]);
        mx=max(mx,dp[i]);
    }
}
 
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(int i=1;i<=n;i++){//从第i行开始加
        memset(b,0,sizeof(b));//归零
        for(int j=i;j<=n;j++){//加到j行
            for(int k=1;k<=n;k++){//第j行各个列的值
                b[j][k]=a[j][k]+b[j-1][k];
            }
            solve(j);
        }
    }
    printf("%d\n",mx);
}

https://blog.csdn.net/mirocky/article/details/104163844?utm_source=app&app_version=4.5.8
最大m子段和只理解了还未整理。

脚踏实地,还需更努力,因为想要的不会凭空而来★

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值