题目描述:
有两个长度为 n 的序列,a0,a1,…,an−1和 b0,b1,…,bn−1。CSL 有一种魔法,每执行一次魔法,可以任意挑选一个序列并任意交换序列中两个元素的位置。CSL 使用若干次魔法,得到最终的序列 a 和 b,并且想要让 a0*b0+a1*b1+…+an−1*bn−1的值最小化。求解 CSL 至少使用多少次魔法,能够达到最小化的目标。
贪心:大配小,可以得到最小。要大配小,就要交换位置。
如何知道要换到哪个位置?我用的离散化,离散化成(1,2,…n)的一个序列,离散化后的b序列每个数字对应要配对的数字可以直接用 n-bi+1计算得出,用数组映射出a的每个元素应该放的位置。
已知每个元素应该放的位置,问题转化为如果用最小交换次数得到目标序列。看似是一个错排问题,首先想到一点:类似模拟的过程,每个点到它要到的位置是一定要和那个位置交换的,如果全部直接对对应位置交换,得到的就直接是最小交换次数。
问题是交换的化变换的是两个点的位置,状态变了,下次交换时就是不同的状态。如何处理呢?用dfs跟踪处理吗?代码能写出来吗?
再进一步思考,如果把每个位置的元素的位置和最终应该放到的位置连起来,可以发现什么?一定是一些不相交的连续的环?这样一来整个问题变得很清晰,首先每个元素都必有一个要去的位置,我们把他当成一条出边,那么每个元素最后总会进到一个环里,也就是说所有的需要交换的最后是一个个的环。
至此,这道题可能可以用图论来解。
那么,一个环要交换多少次能得到对应的结果?假设一个环有n条边,那么需要交换n-1次。简单的拿一个两条边的换验证一下就行了。
那么问题很简单了,求环,然后用把环的点的个数减1然后全部加起来。因为全部是单向边,当时想的就是强连通,然后计每个强连通分量的点的个数。实际上因为环不相交,全是分隔开的简单环,用dfs和直接用循环跳转复杂度相同,直接用循环常数非常小。
那么这题的做法就是:离散化,映射,求环(dfs或tarjan或循环)。
然而我在用tarjan的时候第一次统计答案方式错了,没有通过自己的一组样例。也许当初输错了吧。导致没有尽早A掉。
更换统计方式:发现每有一个环我们要扣掉一个点,总共只有n个点,那么有几个环我们就会扣掉几个点。因此直接用点数减去环数得到答案,交一发A掉这题
还是贴出我的tarjan吧。。。
#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn = 1e6+10;
int a[maxn],b[maxn];
int ta[maxn],tb[maxn];
int pos[maxn];
map<int,int> mpa,mpb;
vector<int> g[maxn];
int dfn[maxn],res,cnt,low[maxn],sta[maxn],belong[maxn],top,vis[maxn];
void tarjan(int s){
low[s]=dfn[s]=++cnt;
sta[++top] = s;
vis[s] = 1;
for(int i = 0; i < g[s].size(); i++){
int v = g[s][i];
if(!dfn[v]) tarjan(v);
if(vis[v])
low[s]=min(low[s],low[v]);
}
if(low[s]==dfn[s]){
++res;
do{
belong[sta[top]]=res;
vis[sta[top]] = 0;
}while(sta[top--]!=s);
}
}
int main(){
scanf("%d",&n);
for(int i = 1; i <= n; i++){
scanf("%d",&a[i]);
ta[i] = a[i];
}
for(int i = 1; i <= n; i++) {
scanf("%d",&b[i]);
tb[i]=b[i];
}
sort(a+1,a+n+1);
for(int i = 1 ;i <= n; i++) mpa[a[i]] = i; //离散化a
for(int i = 1; i <= n; i++) ta[i]=mpa[ta[i]];
sort(b+1,b+1+n);
for(int i = 1 ;i <= n; i++) mpb[b[i]] = i; //离散化b
for(int i = 1; i <= n; i++) tb[i]=mpb[tb[i]];
for(int i = 1; i <= n; i++) pos[n-tb[i]+1] = i; //映射离散化后的a数组每个元素应该放的位置
for(int i = 1; i <= n; i++)
g[i].push_back(pos[ta[i]]); //建图
for(int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i);
cout<<n-res<<endl;
}