您的位置:首页 > 理论基础 > 计算机网络

最小内积 北航第十二届程序设计竞赛网络预赛

2016-12-12 22:01 232 查看

题目描述   ( 题目链接
BUAA 609 )

给定两个
n维向量

A⃗ =(x1,x2,⋯,xn)

B⃗ =(y1,y2,⋯,yn)
,定义它们的点积为

A⃗ ⋅B⃗ =∑1≤i≤nxi⋅yi
对于每个向量,它的每个分量互不相同,而且任意两个分量均可交换位置。

求一种交换策略使得
A⃗ 与

B⃗ 的内积最小,如果有多种满足条件的交换策略,那么还要使得交换总次数最少。

请你给出最少的交换次数及交换后
A⃗ ⋅B⃗ 的值。

输入

第一行包含一个正整数
T,表示有

T组测试数据。

接下来依次给出每组测试数据。对于每组测试数据:

第一行包含一个正整数
n,表示向量维数。

第二行包含
n个整数,第

i
个整数表示
xi。

第三行包含
n个整数,第

i
个整数表示
yi。

保证在一行中的每个整数互不相等,而且每个整数之间有恰好一个空格,没有其他额外的空格。

1≤T≤100,1≤n≤105,0≤|xi|,|yi|≤106

输出

对于每组数据输出一行,包含两个整数,表示
A⃗ 和

B⃗ 的内积和最少的交换次数。

对于本题,输出中在一行的每个整数之间用恰好一个空格隔开,不能有其他额外空格。

输入样例

1
2
1 2
2 1
[/code]

输出样例

4 0
[/code]

出题人是
yic 。

首先考虑问题的第一部分:如何使内积最小?以二维向量为例, (x1,x2)⋅(y1,y2)=x1y1+x2y2
。由于各分量互不相同,不妨设x1<x2,则

(x1,x2)⋅(y1,y2)−(x1,x2)⋅(y2,y1)=x1(y1−y2)+x2(y2−y1)=(x1−x2)(y1−y2)

因此 (x1,x2)⋅(y1,y2)<(x1,x2)⋅(y2,y1)
当且仅当 y1>y2


推广到多维的情况,若存在 i,j(i≠j)
使 xi<xj
且 yi>yj
,则我们可以交换 yi
和 yj
以获得更优的结果,因此最终结果一定有 ∀i≠j∈[1,n](xi−xj)(yi−yj)<0
,即 A⃗ 
的最小分量对应 B⃗ 
的最大分量, A⃗ 
的次小分量对应 B⃗ 
的次大分量,……,以此类推。

知道了最终的对应关系后我们来考虑如何交换,稍加思考可知,对于一对位置 (i,j)
,交换 A⃗ 
与交换 B⃗ 
的结果是一样的,因此可以只交换 A⃗ 
的分量,问题转换为给一个数列,要排列成指定顺序的最少交换次数。

我们从任意一个数出发,找到它的目标位置及这个位置上的数字,再找到新数的目标位置,这样不停找下去,由于不可能有两个不同的数对应同一个位置,因此最终必然形成一个长度为L
的环,不同的环之间没有必要进行交换操作,在同一个环内每把一个数移动到目标位置都会使环的长度减一,总共需要交换
L−1
次。因此最终答案就是 n
减去环的个数。

注意本题没有要求只能对两相邻分量进行交换,因此不用计算逆序对数,另外内积会超过
int
范围,需用
long long
型数据。
附上AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int ma=100005;
int n;

struct node
{
int data;
int xi,yi;
} nodea[ma],nodeb[ma];

bool vis[ma];

int cmp(node a,node b)
{
return a.data<b.data;
}

bool dmp(node a,node b)
{
return a.data>b.data;
}

bool cmp2(node a,node b)
{
return a.xi<b.xi;
}

int countnum(node nodeaa[])
{
int ans=0;
memset(vis,0,sizeof(vis));
for(int i=0; i<n; ++i) ///找环
{
if(vis[i]) continue;
vis[i]=1;
int ne=i;
++ans;
while(nodeaa[ne].yi!=i)
{
ne=nodeaa[ne].yi;
vis[ne]=1;
}
}
return n-ans;
}

int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0; i<n; ++i)
{
scanf("%d",&nodea[i].data);
nodea[i].xi=i;   ///排序前的下标
}
sort(nodea,nodea+n,cmp); ///升序

for(int i=0; i<n; ++i)
{
scanf("%d",&nodeb[i].data);
nodeb[i].xi=i;
}
sort(nodeb,nodeb+n,dmp);

long long ans=0;  ///求最小内积
for(int i=0; i<n; ++i)
ans+=(long long)nodea[i].data*nodeb[i].data;

for(int i=0; i<n; ++i) ///求最小交换次数
nodea[i].yi=nodeb[i].xi;

sort(nodea,nodea+n,cmp2);
long long num=countnum(nodea);

printf("%lld %d\n",ans,num);
}

return 0;
}

/**

10
5
1 2 5 8 0
3 5 9 8 7
71 3

*/


这道题真的是坑死人不偿命,被卡了两天才A出来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  acm BUAA 算法
相关文章推荐