B . Sorting Device [ 问题 7993 ] [ 讨论 ]
Description
After being stuck at home for months because of covid, you decided to explore the contents of your parent’s basement. Among numerous useless items, you found one peculiar object - a sorting device from the sixties that was used to teach sorting algorithms. The device consists of N ordered slots that get initialized with distinct integers once the device is turned on, and a screen for tracking cost. As a user, you can perform swap operations. One swap operation allows you to swap elements at positions i and j for a total cost of A∗|i−j|+B, where A,B are parameters written on the back of the device. Since you’ve been studying your sorting algorithms, you definitely know how to sort the numbers with the smallest possible cost. Right?
Input
The first line contains a single integer N (1≤N≤2⋅105 ) - the number of slots the machine has. The next line has N space-separated integers up to 109 in absolute value that the machine generated after you turned it on. The last line has two positive integers A,B from the machine specs. 1≤A,B≤1000.
Output
In the first line, output the smallest cost needed to sort the sequence. In the second line, output K - the number of swaps needed to do that. In the next K lines output the description of the swaps that need to be done. In each line output two numbers - indices of elements to be swapped, separated by a space. Indices start with one. If two or more sequences have the same total cost, you can output any of them.
Samples
Input 复制
4
42 35 13 21
1 1
Output
7
3
1 3
3 4
2 3
Input 复制
6
6 5 4 3 2 1
5 3
Output
54
3
3 4
2 5
1 6
Source
Waterloo Winter 2021 Local
题意:
给你n个数,让你以两两交换的方式将之排列成升序排列。
每一次交换花费A*|i-j|+B;(A,B题目给出)
思路:贪心:策略:|i-j|相邻减,交换的次数少;
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,A,B,cost;
vector<int> pos,xs;//pos:含义在i位置的值想要去往的位置(人话:就是i上的值升序排完之后应该在哪)
vector<pair<int, int> > ans;//存储每次交换双方的坐标
void swp(int x,int y) {
cost += B + A*abs(y-x);
ans.push_back({x+1,y+1});
swap(pos[x],pos[y]);
return;
}//存储每次交换,以及计算每次交换的花费;
int main() {
cin>>n;
for(int i=0; i<n; i++) {
int x;
scanf("%d",&x);
pos.push_back(x);
xs.push_back(x);
}
cin>>A>>B;
sort(xs.begin(),xs.end());//将输入的值升序排列
for(int i=0; i<n; i++) pos[i] = lower_bound(xs.begin(),xs.end(),pos[i]) - xs.begin();//二分找到每个数排完之后所在的位置,并更新pos值
//for(int i=0;i<n;i++)cout<<pos[i]<<" ";
//cout<<endl;
for(int x=0; x<n; x++) {
if(pos[x] < x) {//相对于原位置更新后的位置需要前移的数
//想,这个原数列中所有的数分两种,前移的数、后退的数,处理完一种整个数列也就处理完了。
vector<int> q;//开个vector来储存交换的坐标。
q.push_back(pos[x]);//最基本的交换就是(x,pos[x])但是显然大多数情况下这种最基本的交换没法一步到位的(就是刚好pos[pos[x]]==x)
//大多数情况上面那个情况的等号是不成立的也就是说牵扯到了其他的数,而下面就是对牵扯到的数进行区分处理;
//cout<<pos[x]<<' ';
while(pos[q.back()] < x) q.push_back(pos[q.back()]);//,cout<<pos[q.back()]<<' ';//pos[pos[x]]<x的
//需要放入,不放入的话在后面再进行交换就会增加耗费。这个也不满足我们的贪心策略(|i-j|相邻)
//举个例子,当x=5 pos[x]=2 pos[pos[x]]=4 4<5:
//显然这个数是需要放进去的,当放进去之后q 中的数 2 4 5;
//也就是 先交换 4 5 在交换 2 4;
//但是不放进去 最终的交换 也在2 4 5 之中进行但是 是先 2 5交换
//然后在进行4 5的交换 显而易见交换次数没变但是耗费增加了
· //以上的情况是满足|i-j|相邻减的贪心策略 其实也满足了 少交换的策略因为上面的处理每次交换只发生在被牵连的数之间。
//而现在想一下为什么是小于的时候往里放而不是大于 试一下会发现 大的加进去 不仅交换次数会变多,耗费也增加了。
//现在想 其实每一次交换都是一次小的排序和整个问题的实质是一样的,可以看做整个大问题的子问题,而通过以上操作使每个子问题都耗费最少。所有的子问题通起来就是整个问题,也即是说整个问题的解决包含了子问题的解决策略。这就保证了我们的策略是正确的。
q.push_back(x);
//cout<<x<<endl;
for(int j=q.size()-1; j>=1; j--) swp(q[j-1],q[j]);
}
}
cout<<cost<<endl<<ans.size()<<endl;
for(int i=0; i<ans.size(); i++) {
printf("%d %d\n",ans[i].first,ans[i].second);
}
for(int i=1;i<=n;i++)cout<<pos[i]<<' ';
return 0;
}
代码源自马**;
贪心算法的正确性证明,,,大家从csdn上看吧。实在是。。