• [HDU6800] Play osu! on Your Tablet


    前言

    有意思的 dp。

    题目

    HDU

    VJudge

    讲解

    (f(i,j)) 表示两个序列末尾是 (i,j) 的最短距离。

    如果 (j<i-1,f(i,j)=f(i-1,j)+dis(i,i-1)),很显然嘛,不然它就转移到 (f(i-1,i)) 去了。

    所以我们只需要单独考虑 (f(i,i-1)) 的转移!

    (g(i)=f(i,i-1))(g(i)=min{f(i-1,j)+dis(i,j)}(jin[0,i-2]))

    (ans=min{min{f(n,i)},g(n)}(iin[1,n-2]))

    我们令 (s(n)=underset{i=2}{overset{n}{sum}} dis(i,i-1)),则有:

    (ans=min{g(i)+s(n)-s(i)}(iin[1,n]))

    真不戳,我们把 (f) 给搞掉了,当然我们也可以把 (g(i)) 的转移换一下:

    (g(i)=min{g(j)+s(i-1)-s(j)+dis(j-1,i)}=min{g(j)-s(j)+dis(j-1,i)}+s(i-1)(jin[0,i-1]))

    我们发现除了 (dis(j-1,i)) ,其它的都很好算出来,所以我们考虑把 (dis(j-1,i)) 拆出来,分四类讨论。

    显然这个 dp 可以用 (operatorname{cdq})分治 优化,其中用树状数组求前缀最值,注意不要忘了后面有一项 (s(i-1))

    时间复杂度为 (O(nlog^2n))

    代码

    //12252024832524
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 100005;
    const LL INF = (1ll << 60);//抱歉0x(3f)^8
    int n;
    LL g[MAXN],h[MAXN],s[MAXN];
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x);
    	if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    struct BIT
    {
    	LL b[MAXN];
    	int lowbit(int x){return (x & -x);}
    	void Add(int x,int len,LL val){for(int i = x;i <= len;i += lowbit(i)) b[i] = Min(b[i],val);}
    	LL Query(int x){LL ret = INF;for(int i = x;i >= 1;i -= lowbit(i)) ret = Min(ret,b[i]);return ret;}
    	void init(int x){for(int i = 1;i <= x;++ i) b[i] = INF;}
    }pre,suf; 
    
    struct node
    {
    	int x,y,px,py,ID,lsh;
    }p[MAXN],q[MAXN];
    bool cmpx1(node a,node b){if(a.px != b.px) return a.px < b.px; return a.ID < b.ID;}
    bool cmpx2(node a,node b){if(a.px != b.px) return a.px > b.px; return a.ID < b.ID;}
    bool cmpy(node a,node b){if(a.py != b.py) return a.py < b.py; return a.ID < b.ID;}
    bool cmpID(node a,node b){return a.ID < b.ID;}
    int dis(int x,int y){return Abs(p[x].x-p[y].x) + Abs(p[x].y-p[y].y);}
    
    void solve(int l,int r)
    {
    	if(l == r) return;
    	int mid = (l+r) >> 1,len = 1;
    	solve(l,mid);
    	for(int i = l;i <= r;++ i)
    		if(p[i].ID <= mid) p[i].px = q[p[i].ID-1].x,p[i].py = q[p[i].ID-1].y;
    		else p[i].px = p[i].x,p[i].py = p[i].y;
    	//situation 1,2 lx<rx ly<ry;lx<rx ly>ry
    	sort(p+l,p+r+1,cmpy);
    	p[l].lsh = 1;
    	for(int i = l+1;i <= r;++ i)
    		if(p[i].py == p[i-1].py) p[i].lsh = p[i-1].lsh;
    		else p[i].lsh = ++len;
    	sort(p+l,p+r+1,cmpx1);
    	pre.init(len); suf.init(len);
    	for(int i = l;i <= r;++ i)
    	{
    		int ID = p[i].ID;
    		if(ID <= mid)//update
    		{
    			pre.Add(p[i].lsh,len,g[ID]+h[ID]-s[ID]-q[ID-1].x-q[ID-1].y);
    			suf.Add(len-p[i].lsh+1,len,g[ID]+h[ID]-s[ID]-q[ID-1].x+q[ID-1].y);
    		}
    		else//query
    		{
    			g[ID] = Min(g[ID],p[i].x+p[i].y+pre.Query(p[i].lsh));
    			g[ID] = Min(g[ID],p[i].x-p[i].y+suf.Query(len-p[i].lsh+1));
    		}
    	}
    	
    	//situation 3,4 lx>rx ly<ry;lx>rx ly>ry
    	pre.init(len); suf.init(len);
    	sort(p+l,p+r+1,cmpx2);
    	for(int i = l;i <= r;++ i)
    	{
    		int ID = p[i].ID;
    		if(ID <= mid)//update
    		{
    			pre.Add(p[i].lsh,len,g[ID]+h[ID]-s[ID]+q[ID-1].x-q[ID-1].y);
    			suf.Add(len-p[i].lsh+1,len,g[ID]+h[ID]-s[ID]+q[ID-1].x+q[ID-1].y);
    		}
    		else//query
    		{
    			g[ID] = Min(g[ID],-p[i].x+p[i].y+pre.Query(p[i].lsh));	
    			g[ID] = Min(g[ID],-p[i].x-p[i].y+suf.Query(len-p[i].lsh+1));
    		}
    	}
    	sort(p+l,p+r+1,cmpID);//You must sort it again,beceuse the following "solve" will use it.
    	solve(mid+1,r);
    }
    
    signed main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	for(int T = Read(); T ;-- T) 
    	{
    		n = Read();
    		for(int i = 1;i <= n;++ i) p[i].x = Read(),p[i].y = Read(),p[i].ID = i,q[i] = p[i];
    		if(n <= 2) {Put(0,'
    ');continue;}
    		h[1] = g[1] = 0;
    		for(int i = 2;i <= n;++ i) s[i] = s[i-1] + dis(i,i-1),h[i] = s[i-1],g[i] = 0;
    		solve(2,n);//pay attention to the boundary
    		LL ans = INF;
    		for(int i = 1;i <= n;++ i) ans = Min(ans,g[i]+h[i]+s[n]-s[i]);
    		Put(ans,'
    ');
    	}
    	return 0;
    }
    //sorry for my poor English
    

    后记

    现在的 dp 好像都套路化了(虽然还是不会),先写出一个暴力转移方程,然后优化就过了。

    dp 难在怎么优化而不是写出方程!

    当然如果连暴力转移都写不出来,那没救了。

  • 相关阅读:
    Redis 笔记
    React Native
    docker run 命令
    vue 集成腾讯地图基础api Demo集合
    在uniapp H5项目中使用腾讯地图sdk
    基于UE4/Unity绘制地图
    腾讯位置服务打车乘客端小车平滑移动-安卓篇
    腾讯位置服务定位打卡功能实现
    腾讯位置服务GPS轨迹回放-安卓篇
    腾讯位置服务个性化图层创建及发布
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/14435256.html
Copyright © 2020-2023  润新知