vijos 1061 迎春舞会之三人组舞

本文深入解析了一道关于迎春舞会三人组舞的算法问题,通过对比不同策略,详细阐述了如何从n个人中选出3m个人进行舞蹈编排,以使‘残疾程度’总和最小。探讨了三种解题思路:动态规划、贪心算法及结合两者的高效求解方案。

题目

背景

H N S D F Z HNSDFZ HNSDFZ的同学们为了庆祝春节,准备排练一场舞

描述

n n n个人选出 3 m 3m 3m个人,排成 m m m组,每组 3 3 3人。
站的队形为较矮的 2 2 2个人站两侧,最高的站中间。
从对称学角度来欣赏,左右两个人的身高越接近,则这一组的“残疾程度”越低。
计算公式为 h = ( a − b ) 2 h=(a-b)^2 h=(ab)2 ( a , b a,b a,b为较矮的2人的身高)
那么问题来了。
现在候选人有 n n n个人,要从他们当中选出 3 m 3m 3m个人排舞蹈,要求总体的“残疾程度”最低。

格式

输入格式

第一行为 m , n m,n m,n
第二行 n n n个数字,为每个人的身高,保证升序排列。

输出格式

输出最小“残疾程度”。

样例输入输出

输入

9 40
1 8 10 16 19 22 27 33 36 40 47 52 56 61 63 71 72 75 81 81 84 88 96 98 103 110 113 118 124 128 129 134 134 139 148 157 157 160 162 164

输出

23

限制

各个测试点 1 s 1s 1s

提示

m ≤ 1000 , n ≤ 5000 m\le1000,n\le5000 m1000,n5000
数据保证 3 m ≤ n 3m\le n 3mn

思路

思路一

这很明显是一道 d p dp dp的题,不难想到 d p [ i , j ] dp[i,j] dp[i,j]表示前 i i i个人组成 j j j个队伍的状态定义。
暴力枚举最后一个组,考虑的因素很多:最高的肯定是每一组中间的那一个,那么最高的可以直接拿来当高子。可是第二高的呢?他有没有可能跟着最高的组成队伍呢?是个问题。开个 b o o l bool bool数组记录?那就变回了 d f s dfs dfs,想都不用想, T L E TLE TLE

思路二

如果在最优答案中出现了这样的组合:
( a , b , e ) (a,b,e) (a,b,e) ( c , d , f ) ( a , b < e (c,d,f)(a,b<e (c,d,f)(a,b<e c , d < f c,d<f c,d<f a < c < b < d ) a<c<b<d) a<c<b<d) h h h的和为 ( b − a ) 2 + ( d − c ) 2 (b-a)^2+(d-c)^2 (ba)2+(dc)2
那么将其替换为
( a , c , e ) (a,c,e) (a,c,e) ( b , d , f ) (b,d,f) (b,d,f)
仍有 a < c < b < e , b < d < f a<c<b<e,b<d<f a<c<b<e,b<d<f
所以h的和为 ( c − a ) 2 + ( d − b ) 2 (c-a)^2+(d-b)^2 (ca)2+(db)2
因为 c − a < b − a c-a<b-a ca<ba d − b < d − c d-b<d-c db<dc
所以 h h h的和一定变小了
如图所示:黑色肯定没有红色的组合好(别忘了保证升序;延伸向右边的线连着高子)黑色肯定没有红色的组合好
所以我选完某一个组合之后,两个矮子中间的就不能再当矮子了。
那高子怎么定?转换思路: d p [ i , j ] dp[i,j] dp[i,j]表示后 i i i个人,而非前 i i i个人
这样剩下的就自然成为了高子咯!
所以状态转移方程就变成了
d p [ i , j ] = m i n ( d p [ k + 1 , j − 1 ] + ( a [ k ] − a [ i ] ) 2 , d p [ i + 1 , j ] ) dp[i,j]=min(dp[k+1,j-1]+(a[k]-a[i])^2,dp[i+1,j]) dp[i,j]=min(dp[k+1,j1]+(a[k]a[i])2,dp[i+1,j])
辅以一点点小技巧:滚动数组+斜率优化。大佬肯定早就不稀罕用了
实现见代码一。

思路三

这还是有点难啊……就没有一个更简单的方法吗?
当然有!听说下雨天,dp和贪心更配哦
倘若你选了第i个人,并在 i + 1 i+1 i+1 n n n中寻找他的队友时,你有没有想过,为什么不直接让 i i i i + 1 i+1 i+1匹配呢?你要是选了 k ( k > i + 1 ) k(k>i+1) k(k>i+1) i + 1 i+1 i+1 k − 1 k-1 k1之间没有人当矮子,你为什么不让 i + 1 i+1 i+1当矮子?
至于高子的问题,你只要让 3 j ≤ i 3j\le i 3ji就可以了,这样选完之后自动会剩下几个用来当高子。
(其实此处证明的不够严谨,但是你可以想象到:最前面的 j − 1 j-1 j1组必然占用 3 j − 3 3j-3 3j3个人,再加上我们现在正在计算的这两个矮子,共有 3 j − 1 3j-1 3j1个人会被使用。那么除去这些人,剩下的就是 i − 3 j + 1 i-3j+1 i3j+1个人,只要 3 j ≤ i 3j\le i 3ji i − 3 j + 1 > 0 i-3j+1>0 i3j+1>0,就一定还有人没有被占用,而 i i i i + 1 i+1 i+1是右数i个中最矮的两个,所以剩下的那个人可以当高子)
实现见代码二

代码

代码一

#include <cstdio>
#include <iostream>
using namespace std;
   
typedef long long LL;
const LL llong_inf=(1ll<<62)-1;
# define For(i,a,b) for(register int i=(a);i<=(b);++i)
# define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
   
inline int readint(){
    int a=0;char c=getchar();bool f=0;
    while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
    while('0'<=c&&c<='9') a=a*10+c-'0',c=getchar();
    if(f) a=-a;return a;
}
inline LL Pow(const LL &x){
    return x*x;
}
  
struct Point{
    LL x,y;
    Point(LL X=0,LL Y=0):x(X),y(Y){}
};
 
Point operator-(const Point &a,const Point &b){
    return Point(a.x-b.x,a.y-b.y);
}
 
LL operator*(const Point &a,const Point &b){
    return a.x*b.y-b.x*a.y;
}
 
const int MAXN=5002;
int num[MAXN],q[MAXN],head,tail;
LL dp[MAXN][2];
Point point[MAXN];
 
void Push(const int &Index){
    Point now,last;
    while(tail-1>=head)
    {
        now=point[Index]-point[q[tail]];
        last=point[q[tail]]-point[q[tail-1]];
        if(now*last>=0)
            --tail;
        else
            break;
    }
    q[++tail]=Index;
}
  
void Maintain(const Point &xielv){
    Point now;
    while(head+1<=tail)
    {
        now=point[q[head+1]]-point[q[head]];
        if(now*xielv>=0)
            ++head;
        else
            break;
    }
}
  
int main()
{
    int m=readint(),n=readint();
    For(i,1,n) num[i]=readint();
    dp[n-1][1]=llong_inf;
    Rep(i,n-2,1) dp[i][1]=min(dp[i+1][1],Pow(num[i+1]-num[i]));
    int now_dp=1;
    For(j,2,m)
    {
        now_dp^=1;
        head=1,tail=0;
        int origin=n-3*j+1;
        point[origin]=Point(2*num[origin],dp[origin+1][now_dp^1]+Pow(num[origin]));
        dp[origin][now_dp]=dp[origin+2][now_dp^1]+Pow(num[origin+1]-num[origin]);
        Rep(i,origin-1,1)
        {
            Push(i+1);
            Maintain(Point(1,num[i]));
            dp[i][now_dp]=dp[q[head]+1][now_dp^1]+Pow(q[head]-num[i]);
            dp[i][now_dp]=min(dp[i][now_dp],dp[i+1][now_dp]);
            point[i]=Point(2*num[i],dp[i+1][now_dp^1]+Pow(num[i]));
        }
    }
    cout<<dp[1][now_dp];
    return 0;
}

代码二

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
    
inline int readint(){int x;scanf("%d",&x);return x;}
inline long long sqr(const long long &x){return x*x;}//pow(x,2)

const int MAXN=5000,MAXM=1000;const long long llong_inf=(1ll<<61)-1;
long long dp[MAXN+2][MAXM+2];int a[MAXN+2];

int main()
{
    int m=readint(),n=readint();
    for(int i=1;i<=n;++i)
    {
		a[i]=readint();
		for(int j=1;j<=m;++j)
			dp[i][j]=llong_inf;
	}
    for(int j=1;j<=m;++j)
    	for(int i=n-3*j+1;i>=1;--i)
    		dp[i][j]=min(dp[i][j],min(dp[i+2][j-1]+sqr(a[i+1]-a[i]),dp[i+1][j]));
    long long ans=llong_inf;
    for(int i=n-3*m+1;i>=1;--i)
    	ans=min(ans,dp[i][m]);
    printf("%lld",ans);
    return 0;
}

后记

其实一开始并没有想到简单的思路三,而是真的老老实实地打了一个斜率优化的 d p dp dp……只能说,好的思路造就较少代码量与较低错误率,但学的东西多了以后,就算不是正解思路也能搞出个名堂来!

内容概要:本文主要介绍了一个基于Matlab实现的无机空中通信仿真项目,旨在通过数值仿真手段研究无机在空中作为通信节点时的通信性能、信号传播特性和网络拓扑行为。该仿真涵盖了无机飞行轨迹建模、无线信道建模(如路径损耗、多普勒效应、阴影衰落等)、通信链路建立与中断判断、信号干扰分析以及网络性能评估(如吞吐量、延迟、连接可靠性等)。项目可能结合优化算法或智能控制策略,用于优化无机位置部署或动态路径规划,以提升通信服务质量。整个仿真系统为研究员提供了一套完整的工具链,用于验证新型无机通信协议、协作机制和网络架构的有效性。; 适合群:具备一定Matlab编程基础和通信原理基础知识,从事无机、无线通信、网络优化等相关领域研究的研发员和高校研究生。; 使用场景及目标:① 评估无机作为空中基站或中继节点的通信覆盖能力和网络性能;② 设计和优化无机集群的通信拓扑与协同策略;③ 验证新型无线资源分配、移动性管理和抗干扰算法在动态空地网络中的有效性。; 阅读建议:使用者应结合Matlab代码深入理解仿真模型的构建逻辑,重点关注通信信道模块和无机运动学模型的耦合关系,并可根据实际研究需求,对仿真参数(如环境噪声、飞行速度、天线增益)进行调整,以开展针对性的对比实验和性能分析。
内容概要:本文围绕微电网中光伏发电系统经逆变器带负载的完整仿真模型展开研究,利用Simulink平台构建了从光伏阵列建模、DC-AC逆变器控制(包括PWM调制与电压电流双闭环控制)、并网策略到负载响应的全过程仿真系统。重点分析了系统在不同工况下的动态响应特性与电能质量表现,并对并网控制策略、最大功率点跟踪(MPPT)技术及系统稳定性进行了深入探讨和验证。该模型不仅可用于教学演示微电网的基本架构与运行机制,更为科研提供了可靠的仿真平台,支持对新型控制算法与系统优化方案的有效验证与评估。; 适合群:具备一定电力电子技术、自动控制理论基础及Simulink/MATLAB操作经验的电气工程、自动化等相关专业的本科生、研究生及科研员。; 使用场景及目标:①用于高校课程教学中微电网系统结构与运行原理的直观演示;②为科研工作者提供光伏发电并网系统的仿真验证平台,支持开展逆变器控制算法(如双闭环控制、MPPT)、系统稳定性分析及电能质量管理等关键技术的研究与优化。; 阅读建议:建议学习者结合Simulink仿真环境动手搭建模型,重点关注各功能模块间的信号传递关系与关键参数设置,并通过调整光照强度、温度、负载大小等外部条件,观察系统动态响应过程,从而深化对微电网运行特性的理解与掌握。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值