P2091 排序 题解 https://www.luogu.org/recordnew/show/16120620
题目描述
小A有n个物件排成一排,每个物件有它的体积V和质量M。n个物件的体积在1~n内,且各不相同,但质量可能相同。
现在,小A需要把n个物件按体积从小到大重新排列。他的排序方式是:每次交换两个物件。这样会他会消耗的体力值为两个物件的质量和。
小A想知道,为了将物件排序,他消耗的最少体力值是多少?
输入输出格式
输入格式:
第一行,一个正整数n,表示物件的数量。
第二行n个正整数,第i个数表示从左到右第i个物品的体积。
第三行n个正整数,第i个数表示从左到右第i个物品的质量
输出格式:
一个数,表示小A消耗的最小体力值
输入输出样例
输入样例#1: 复制
3
1 3 2
2 2 3
输出样例#1: 复制
5
说明
注意的地方:
1. v m都应该是long long型;
2 v并不是连续的整数,所以要离散化处理
3.
①暴力分解置换群成若干轮换
②对于每个轮换单独处理:
1、求出这个轮换中最小值m‘和这个轮换中各个元素的总和s以及个数n,则上述第一种方案的代价为:Cost1=m’*(n-1)+s-m’
这里的意思是使用m最小的那个逐一将别人换到应该的位置上,共需要n-1次置换。
2、求出整个数组中最小值m,则上述第二种方案的代价为:Cost2=m+m’+m*(n-1)+s-m’+m+m’【这里原来给出的是错误的】
m+m‘是从循环节外部换入换出,
3、去Cost1和Cost2中的最小值作为此轮换处理结果,加入到最后结果中。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
#define MAX 200010
#define Imax 1000000000000
typedef struct node
{
long long v;
long long m;
long long id;
bool vd;
} node;
int n;
node dat[MAX];
bool cmp(node &a,node &b)
{
return a.v<b.v;
}
int main()
{
cin>>n;
long long res=0;
long long allmin=Imax;
for(int i=1; i<=n; i++)
{
cin>>dat[i].v;
dat[i].vd=false;
dat[i].id=i;
}
for(int i=1; i<=n; i++)
{
cin>>dat[i].m;
allmin=min(dat[i].m,allmin);
}
sort(dat+1,dat+n+1,cmp);
for(int i=1; i<=n; i++)
{
if(dat[i].vd) continue;
if(dat[i].id==i) continue;
int j=i;
long long imax=0,imin=Imax;
long long sum=0;
vector<long long> temp;
temp.push_back(dat[j].m);
dat[j].vd=true;
do {
j=dat[j].id;
temp.push_back(dat[j].m);
dat[j].vd=true;
} while(dat[j].id!=i);
for(int k=0; k<temp.size(); k++)
{
sum+=temp[k];
imin=min(imin,temp[k]);
}
/*
1、求出这个轮换中最小值m‘和这个轮换中各个元素的总和s以及个数n,则上述第一种方案的代价为:Cost1=m’*(n-1)+s-m’
2、求出整个数组中最小值m,则上述第二种方案的代价为:Cost2=m+m’+m*(n-1)+s-m'+m+m’
*/
imin=min(imin*(temp.size()-2)+sum,allmin*(temp.size()-1)+sum-imin+2*(imin+allmin));
res+=imin;
}
cout<<res<<endl;
return 0;
}