2020 Multi-University Training Contest 3---- HDU--6800、Play osu! on Your Tablet (数据结构优化dp)

本文主要是介绍2020 Multi-University Training Contest 3---- HDU--6800、Play osu! on Your Tablet (数据结构优化dp),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目链接

题面:
在这里插入图片描述

题意:
你需要按照给定的顺序点击 n n n 个点,每个点都有他的坐标。
有两只手指可以用,某个点被其中任意一只手指点击即可。

每只手指第一次点击不需要花费,第一次之后每次点击的花费等于当前点击的点和上一个点击的点的曼哈顿距离。问你点击完所有点的最小花费。

题解:
我们设 d i s ( i , j ) dis(i,j) dis(i,j) 为第 i i i 个点到第 j j j 个点的曼哈顿距离。
d p [ i ] [ j ] dp[i][j] dp[i][j] 为点击了前 i i i 个点且一根手指放在第 i i i 个点,另一根手指放在第 j j j 个点的最小代价。
初始时, d p [ 1 ] [ j ] = 0 dp[1][j]=0 dp[1][j]=0,表示两根手指的起始位置分别是第 i i i 个点和第 j j j 个点。

考虑转移:
d p [ i + 1 ] [ j ] = m i n ( d p [ i + 1 ] [ j ] , d p [ i ] [ j ] + d i s ( i , i + 1 ) ) dp[i+1][j]=min(dp[i+1][j],dp[i][j]+dis(i,i+1)) dp[i+1][j]=min(dp[i+1][j],dp[i][j]+dis(i,i+1)) 表示手指从第 i i i 个点到第 i + 1 i+1 i+1 个点。
d p [ i + 1 ] [ i ] = m i n ( d p [ i + 1 ] [ i ] , d p [ i ] [ j ] + d i s ( j , i + 1 ) ) dp[i+1][i]=min(dp[i+1][i],dp[i][j]+dis(j,i+1)) dp[i+1][i]=min(dp[i+1][i],dp[i][j]+dis(j,i+1)) 表示手指从第 j j j 个点到第 i + 1 i+1 i+1 个点。

注意到第一种转移相当于给每个 j j j 对应的信息增加定值,第二种转移相当于对所有 j j j 求出信息最小值后对单个 d p dp dp 值进行更新,这启发我们使用数据结构维护信息。

我们设 f [ i ] [ j ] = d p [ i ] [ j ] − ∑ k = 1 i − 1 d i s ( k , k + 1 ) f[i][j]=dp[i][j]-\sum\limits_{k=1}^{i-1}dis(k,k+1) f[i][j]=dp[i][j]k=1i1dis(k,k+1)

则转移变为以下两个:
①、 f [ i + 1 ] [ j ] = m i n ( f [ i + 1 ] [ j ] , f [ i ] [ j ] ) f[i+1][j]=min(f[i+1][j],f[i][j]) f[i+1][j]=min(f[i+1][j],f[i][j])

f [ i + 1 ] [ j ] = d p [ i + 1 ] [ j ] − ∑ k = 1 i d i s ( k , k + 1 ) = d p [ i ] [ j ] + d i s ( i , i + 1 ) − ∑ k = 1 i d i s ( k , k + 1 ) = d p [ i ] [ j ] − ∑ k = 1 i − 1 d i s ( k , k + 1 ) = f [ i ] [ j ] f[i+1][j]=dp[i+1][j]-\sum\limits_{k=1}^{i}dis(k,k+1)=dp[i][j]+dis(i,i+1)-\sum\limits_{k=1}^{i}dis(k,k+1)=dp[i][j]-\sum\limits_{k=1}^{i-1}dis(k,k+1)=f[i][j] f[i+1][j]=dp[i+1][j]k=1idis(k,k+1)=dp[i][j]+dis(i,i+1)k=1idis(k,k+1)=dp[i][j]k=1i1dis(k,k+1)=f[i][j]

②、 f [ i + 1 ] [ i ] = m i n ( f [ i + 1 ] [ j ] , f [ i ] [ j ] + d i s ( j , i + 1 ) − d i s ( i , i + 1 ) ) f[i+1][i]=min(f[i+1][j],f[i][j]+dis(j,i+1)-dis(i,i+1)) f[i+1][i]=min(f[i+1][j],f[i][j]+dis(j,i+1)dis(i,i+1))

f [ i + 1 ] [ i ] = d p [ i + 1 ] [ i ] − ∑ k = 1 i d i s ( k , k + 1 ) = d p [ i ] [ j ] + d i s ( j , i + 1 ) − ∑ k = 1 i d i s ( k , k + 1 ) = d p [ i ] [ j ] − ∑ k = 1 i − 1 d i s ( k , k + 1 ) + d i s ( j , i + 1 ) − d i s ( i , i + 1 ) = f [ i ] [ j ] + d i s ( j , i + 1 ) − d i s ( i , i + 1 ) f[i+1][i]=dp[i+1][i]-\sum\limits_{k=1}^{i}dis(k,k+1)=dp[i][j]+dis(j,i+1)-\sum\limits_{k=1}^{i}dis(k,k+1)=dp[i][j]-\sum\limits_{k=1}^{i-1}dis(k,k+1)+dis(j,i+1)-dis(i,i+1)=f[i][j]+dis(j,i+1)-dis(i,i+1) f[i+1][i]=dp[i+1][i]k=1idis(k,k+1)=dp[i][j]+dis(j,i+1)k=1idis(k,k+1)=dp[i][j]k=1i1dis(k,k+1)+dis(j,i+1)dis(i,i+1)=f[i][j]+dis(j,i+1)dis(i,i+1)

我们将上两式写成等式。
(1) f [ i + 1 ] [ j ] = f [ i ] [ j ] f[i+1][j]=f[i][j] f[i+1][j]=f[i][j]
(2) f [ i + 1 ] [ i ] = m i n ( f [ i ] [ j ] + d i s ( j , i + 1 ) − d i s ( i , i + 1 ) ) f[i+1][i]=min(f[i][j]+dis(j,i+1)-dis(i,i+1)) f[i+1][i]=min(f[i][j]+dis(j,i+1)dis(i,i+1))

对于上式中的 i , j i,j i,j 均满足 j < i j<i j<i,因为最后一下点击在 i i i,另一下点击不可能在 i i i,且一定在 i i i 之前。
假设我们已经求得 f [ n ] [ j ] f[n][j] f[n][j] 那么 a n s = m i n ( f [ n ] [ j ] ) + ∑ k = 1 n − 1 d i s ( k , k + 1 ) ans=min(f[n][j])+\sum\limits_{k=1}^{n-1}dis(k,k+1) ans=min(f[n][j])+k=1n1dis(k,k+1)

考虑怎么求解 f f f
我们发现可以枚举第一维 i i i,只维护第二维 j j j
考虑 f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j] 对于所有的 j < i j<i j<i 均可以由 f [ i ] [ j ] f[i][j] f[i][j] 转移而来。即这一部分可以不用动。
唯一新增的状态就是 f [ i + 1 ] [ i ] f[i+1][i] f[i+1][i]

f [ i + 1 ] [ i ] = m i n ( f [ i ] [ j ] + d i s ( j , i + 1 ) − d i s ( i , i + 1 ) ) f[i+1][i]=min(f[i][j]+dis(j,i+1)-dis(i,i+1)) f[i+1][i]=min(f[i][j]+dis(j,i+1)dis(i,i+1)) 其中 d i s ( i , i + 1 ) dis(i,i+1) dis(i,i+1) 是定值,单独计算。
即求解 m i n ( f [ i ] [ j ] + d i s ( j , i + 1 ) ) min(f[i][j]+dis(j,i+1)) min(f[i][j]+dis(j,i+1))

我们将 ( f [ i ] [ j ] + d i s ( j , i + 1 ) ) (f[i][j]+dis(j,i+1)) (f[i][j]+dis(j,i+1)),看作一个三维坐标点 ( x [ j ] , y [ j ] , f [ i ] [ j ] ) (x[j],y[j],f[i][j]) (x[j],y[j],f[i][j])

那么当前要求解 ( x [ i + 1 ] , y [ i + 1 ] , 0 ) (x[i+1],y[i+1],0) (x[i+1],y[i+1],0) j < i j<i j<i 的所有的三维坐标点的最小距离。
这个距离定义为 前两维为曼哈顿距离,第三维为 f [ i ] [ j ] f[i][j] f[i][j] 的值。

假设这个最小值为 a n s ans ans,那么再将 ( x [ i ] , y [ i ] , a n s − d i s ( i , i + 1 ) ) (x[i],y[i],ans-dis(i,i+1)) (x[i],y[i],ansdis(i,i+1)) 保存为一个已知的三维坐标点即可。

这里有一种特殊情况需要处理一下,就是 a n s ≤ 0 ans\le0 ans0 恒成立,所以求解出的 a n s ans ans 要对 0 0 0 取个 m i n min min
这是因为 f [ i + 1 ] [ i ] f[i+1][i] f[i+1][i] 有可能由 f [ i ] [ 0 ] f[i][0] f[i][0] 转移过来,此时 f [ i ] [ j ] + d i s ( j , i + 1 ) = 0 f[i][j]+dis(j,i+1)=0 f[i][j]+dis(j,i+1)=0
因为此时 d i s ( j , i + 1 ) = 0 dis(j,i+1)=0 dis(j,i+1)=0(第一次动第二根手指) 且 f [ i ] [ j ] = f [ i ] [ 0 ] = d p [ i ] [ 0 ] − ∑ k = 1 i − 1 d i s ( k , k + 1 ) = 0 f[i][j]=f[i][0]=dp[i][0]-\sum\limits_{k=1}^{i-1}dis(k,k+1)=0 f[i][j]=f[i][0]=dp[i][0]k=1i1dis(k,k+1)=0

可以维护这个问题的数据结构有很多(题解上说很多),而且还可以离线用分治解决。

这里选择用 kd-tree 来维护某个点到前面已知点的最短距离。
因为这个最短距离的特殊性,我们需要改动一些东西。

人丑常数大的 kd-tree。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<unordered_set>
#include<set>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
//#define lc (cnt<<1)
//#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
#define fhead(x) for(int i=head[(x)];i;i=nt[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const double alpha=0.75;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=100100;
const int maxm=100100;
const int maxp=100100;
const int up=30;struct Point
{ll x[3];
}po[maxn],p[maxn];struct node
{ll p[3];int lc,rc;ll mi[3],ma[3];int si;void init(void){lc=rc=si=0;mi[0]=mi[1]=mi[2]=0;ma[0]=ma[1]=ma[2]=0;}
}t[maxn];int root,cnt,sum,now_maxx=3;
//root kd-tree 的根
//cnt kd-tree 节点编号
//sum 重构节点编号
//now_maxx 维度ll res[3];//查询节点
ll in[3]; //插入节点
ll ans;   //最优答案
int hui[maxn],top;//回收节点
int nowd;  // 第几维
int dd,*pp;//重构bool cmp(const Point &a,const Point &b)
{return a.x[nowd]<b.x[nowd];
}bool balance(int q)
{return (double)t[t[q].lc].si<=t[q].si*alpha&&(double)t[t[q].rc].si<=t[q].si*alpha;
}int newnode(int k=0)
{int p=0;if(top) p=hui[top--];else p=++cnt;t[p].si=t[p].lc=t[p].rc=0;//k=0 insert 插入//k!=0 rebuild  插入for(int i=0;i<now_maxx;i++)t[p].mi[i]=t[p].ma[i]=t[p].p[i]=(k==0?in[i]:po[k].x[i]);return p;
}void  pushup(int p)
{int lc=t[p].lc,rc=t[p].rc;for(int i=0;i<now_maxx;i++){if(lc) t[p].mi[i]=min(t[p].mi[i],t[lc].mi[i]),t[p].ma[i]=max(t[p].ma[i],t[lc].ma[i]);if(rc) t[p].mi[i]=min(t[p].mi[i],t[rc].mi[i]),t[p].ma[i]=max(t[p].ma[i],t[rc].ma[i]);}t[p].si=t[lc].si+t[rc].si+1;
}int build(int l,int r,int d)
{if(l>r) return 0;int mid=(l+r)>>1;nowd=d;nth_element(po+l,po+mid,po+r+1,cmp);int p=newnode(mid);t[p].lc=build(l,mid-1,(d+1)%now_maxx);t[p].rc=build(mid+1,r,(d+1)%now_maxx);pushup(p);return p;
}void _insert(int &p,int d)
{if(!p){pp=NULL;p=newnode();return ;}if(in[d]<t[p].p[d])_insert(t[p].lc,(d+1)%now_maxx);else _insert(t[p].rc,(d+1)%now_maxx);pushup(p);if(!balance(p)) pp=&p,dd=d;
}void dfs(int p)
{if(t[p].lc) dfs(t[p].lc);++sum;for(int i=0;i<now_maxx;i++)po[sum].x[i]=t[p].p[i];hui[++top]=p;if(t[p].rc) dfs(t[p].rc);
}void rebuild(void)
{sum=0;dfs(*pp);(*pp)=build(1,sum,dd);
}void _insert(void)
{_insert(root,0);if(pp!=NULL) rebuild();
}//这个题的dis有点特殊
//第1维和第2维是曼哈顿距离,第3维是直接加第3维的权值
inline ll dis(int p)
{ll ans=0;for(int i=0;i<now_maxx-1;i++)ans+=1ll*abs(t[p].p[i]-res[i]);ans+=1ll*t[p].p[now_maxx-1];return ans;
}//同样我们在某一棵子树上找最小值的时候
//前两维是曼哈顿距离,第3维是直接加第3维的权值
inline ll di(int p)
{ll ans=0;for(int i=0;i<now_maxx-1;i++)ans+=max(t[p].mi[i]-res[i],0)+max(res[i]-t[p].ma[i],0);ans+=t[p].mi[now_maxx-1];return ans;
}void ask(int p)
{if(!p) return ;if(dis(p)<ans) ans=dis(p);ll dl=lnf,dr=lnf;if(t[p].lc) dl=di(t[p].lc);if(t[p].rc) dr=di(t[p].rc);if(dl<dr){if(dl<ans)ask(t[p].lc);if(dr<ans)ask(t[p].rc);}else{if(dr<ans)ask(t[p].rc);if(dl<ans)ask(t[p].lc);}
}void init(int n)
{root=0,sum=0,cnt=0;top=0;for(int i=1;i<=cnt;i++)t[i].init();
}int main(void)
{int tt;scanf("%d",&tt);while(tt--){int n;scanf("%d",&n);ll sum=0;ll minn=lnf;for(int i=1;i<=n;i++){scanf("%lld%lld",&p[i].x[0],&p[i].x[1]);if(i!=1)sum+=abs(p[i].x[0]-p[i-1].x[0])+abs(p[i].x[1]-p[i-1].x[1]);}if(n<=2){printf("0\n");continue;}for(int i=2;i<=n;i++){res[0]=p[i].x[0],res[1]=p[i].x[1],res[2]=0;ans=lnf;ask(root);//考虑 f[i+1][i] 直接由 f[i][0] 转移过来,此时ans应为0//因为://此时 dis(0,i+1)  是第二根手指的第一步,不需要计算//f[i][0]=dp[i][0]-sum_k_from_1_to_i-1 dis(k,k+1)//而dp[i][0]=sum_k_from_1_to_i-1 dis(k,k+1)ans=min(ans,0);ans=ans-(abs(p[i].x[0]-p[i-1].x[0])+abs(p[i].x[1]-p[i-1].x[1]));in[0]=p[i-1].x[0],in[1]=p[i-1].x[1],in[2]=ans;_insert();minn=min(minn,ans+sum);}printf("%lld\n",minn);init(n);}return 0;
}

这篇关于2020 Multi-University Training Contest 3---- HDU--6800、Play osu! on Your Tablet (数据结构优化dp)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/187951

相关文章

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b