• BZOJ 1237 配对


    Description

    你有(n)个整数(A_{i})(n)个整数(B_{i})。你需要把它们配对,即每个(A_{i})恰好对应一 个(Bp_{i})。要求所有配对的整数差的绝对值之和尽量小,但不允许两个相同的数配对。例如(A=lbrace 5,6,8 brace)(B=lbrace 5,7,8 brace),则最优配对方案是(5)(8)(6)(5)(8)(7),配对整数的差的绝对值分别为(2, 2, 1),和为(5)。注意,(5)(5)(6)(7)(8)(8)是不允许的,因为相同的数不许配对。

    Input

    第一行为一个正整数(n),接下来是(n)行,每行两个整数(A_{i})(B_{i}),保证所有(A_{i})各不相同,(B_{i})也各不相同。

    Output

    输出一个整数,即配对整数的差的绝对值之和的最小值。如果无法配对,输出(-1)

    Sample Input

    3
    3 65
    45 10
    60 25

    Sample Output

    32

    HINT

    (30\%)的数据满足:(n le 10^{4})
    (100\%)的数据满足:(1 le n le 10^{5})(A_{i})(B_{i})均为(1)(10^{6})之间的整数。

    难得又一道自己想出的题目。
    首先如果没有相同的数字不能同时配对,那么排序之后两两配对一定是最优的。那么我们在满足题目限制一定也要尽可能的满足题目的限制,相邻两个进行交换。
    我们令(pos_{i})表示前第(i)个排序后数字相同配对的位置,(f_{i,0/1})表示前(i)个数字相同的配对,使其合法的最小增加代价((0)表示与前面的交换,(1)表示与后面的交换)。转移有这样以下的几个:

    [f_{i,1} = min(f_{i-1,0},f_{i-1,1})+calc(pos_{i},pos_{i}+1) ]

    (pos_{i-1} ge pos_{i}-1)时$$f_{i,0} = f_{i-1,0}+calc(pos_{i}-1,pos_{i})$$
    否则$$f_{i,0} = min(f_{i-1,0},f_{i-1,1})+calc(pos_{i}-1,pos_{i})$$
    产生分歧的原因是如果(pos_{i-1} ge pos_{i}-1),那么如果(i-1)号非法配对与右边的交换,(i)号与左边的交换代价计算就会出错。
    (i ge 2)并且(pos_{i}=pos_{i-1}+1),我们可以两个非法的进行交换,得到以下的转移:
    (pos_{i-2} ge pos_{i-1}-1)时,$$f_{i,0} = min(f_{i,0},f_{i-2,0}+calc(pos_{i-1},pos_{i}))$$
    否则$$f_{i,0} = min(f_{i,0},min(f_{i-2,0},f_{i-2,1})+calc(pos_{i-1},pos_{i}))$$
    分歧的原因同上。

    以上转移(calc(a,b))为计算交换(a,b)位置增加代价的函数。

    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    
    typedef long long ll;
    #define inf (1LL<<60)
    #define maxn 100010
    int n,pos[maxn],tot; ll sum,A[maxn],B[maxn],f[maxn][2];
    
    inline ll calc(int a,int b)
    {
    	if (!a||!b) return inf; if (a > n||b > n) return inf;
    	return (abs(A[a]-B[b])+abs(A[b]-B[a]))-(abs(A[a]-B[a])+abs(A[b]-B[b]));
    }
    inline void dp()
    {
    	memset(f,0x7,sizeof(f));
    	f[0][0] = f[0][1] = 0;
    	for (int i = 1;i <= tot;++i)
    	{
    		f[i][1] = min(f[i-1][0],f[i-1][1])+calc(pos[i],pos[i]+1);
    		if (pos[i-1]<pos[i]-1) f[i][0] = min(f[i-1][0],f[i-1][1])+calc(pos[i]-1,pos[i]);
    		else f[i][0] = f[i-1][0]+calc(pos[i]-1,pos[i]);
    		if (i >= 2&&pos[i] == pos[i-1]+1)
    		{
    			if (pos[i-2]<pos[i-1]-1) f[i][0] = min(f[i][0],min(f[i-2][0],f[i-2][1])+calc(pos[i-1],pos[i]));
    			else f[i][0] = min(f[i][0],f[i-2][0]+calc(pos[i-1],pos[i]));
    		}
    	}
    }
    
    int main()
    {
    	freopen("1237.in","r",stdin);
    	freopen("1237.out","w",stdout);
    	scanf("%d",&n); for (int i = 1;i <= n;++i) scanf("%lld %lld",A+i,B+i);
    	A[0] = A[n+1] = inf; pos[0] = -100;
    	sort(A+1,A+n+1); sort(B+1,B+n+1);
    	for (int i = 1;i <= n;++i)
    	{
    		sum += abs((A[i]-B[i]));
    		if (A[i] == B[i]) pos[++tot] = i;
    	}
    	dp(); printf("%lld",sum+min(f[tot][0],f[tot][1]));
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    css backgroud属性与雪碧技术
    css margin 外边距塌陷问题
    css 浮动元素与清除浮动
    css 盒模型的概念与使用
    七年iOS架构师教你如何一举拿下35K的Offer,(附面试技巧)
    iOS开发者月薪想要突破30K,需要经历+提升些什么?
    月薪 8K 与30K的程序员 区别到底在哪里?
    那些月薪35K以上的iOS开发者 都掌握了什么技能?
    从事 iOS 开发8年的面经——送给准备跳槽的你!
    想进BAT大厂的 iOS程序员,看完这个你还觉得Offer难拿吗???
  • 原文地址:https://www.cnblogs.com/mmlz/p/4394922.html
Copyright © 2020-2023  润新知