zoukankan      html  css  js  c++  java
  • [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 难在怎么优化而不是写出方程!

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

  • 相关阅读:
    leetcode 78. 子集 JAVA
    leetcode 91. 解码方法 JAVA
    leetcode 75. 颜色分类 JAVA
    leetcode 74 搜索二维矩阵 java
    leetcode 84. 柱状图中最大的矩形 JAVA
    last occurance
    first occurance
    classical binary search
    LC.234.Palindrome Linked List
    LC.142. Linked List Cycle II
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/14435256.html
Copyright © 2011-2022 走看看