1.先介绍快排,在洛谷的这题中,所用的快排要比平时写的快排快才能AC,所以这里给出题目和AC代码
快速排序
#include<iostream>
#include<string.h>
using namespace std;
int n, a[1000005];
void quicksort(int *a, int s, int e){//应用二分思想
int mid = a[s+(e - s)/2];//中间数
int i = s, j = e;
while(i <= j){//这里注意要有=
while(a[i] < mid) i++;//查找左半部分比中间数大的数
while(a[j] > mid) j--;//查找右半部分比中间数小的数
if(i <= j){//如果有一组不满足排序条件(左小右大)的数
swap(a[i], a[j]);//交换
i++;
j--;
}
}
if(s < j){//递归搜索左半部分
quicksort(a, s, j);
}
if(i < e){//递归搜索右半部分
quicksort(a, i, e);
}
}
int main(){
cin>>n;
for(int i = 0; i < n; i++){
cin>>a[i];
}
quicksort(a, 0, n-1);
for(int i = 0; i < n-1; i++){
cout<<a[i]<<" ";
}
cout<<a[n-1]<<endl;
return 0;
}
2.接下来是求排列数的逆序对,即给出排列好的数,不改变排列数的顺序,如果下标i < j 则元素也应该a[i] < a[j] ,否则为逆序对,这题是为了计算逆序对的数量.
逆序对
#include<iostream>
#include<string.h>
using namespace std;
typedef long long ll;
int n;
ll ans = 0;
int a[100005], b[100005];
void merge(int *a, int s, int mid, int e, int *b){
int p1 = s, p2 = mid+1, ss = 0;
while(p1 <= mid && p2 <= e){
if(a[p1] > a[p2]){//如果满足逆序的条件,所做的计数
ans = ans + e - p2 + 1;
b[ss++] = a[p1++];
}else{
b[ss++] = a[p2++];
}
}
while(p1 <= mid){
b[ss++] = a[p1++];
}
while(p2 <= e){
b[ss++] = a[p2++];
}
for(int i = 0; i < e-s+1; i++){//这里的意思是把数组范围内,排好序之后的数,都赋值给原数组
a[s+i] = b[i];
}
}
void MergeSortAndcount(int *a, int s, int e, int *b){//先分一半,排好序,然后再 MergeSortAndcount(a);去计算左边取一个和右边取一个逆序数的数量
if(s < e){
int mid = s + (e - s)/2;
MergeSortAndcount(a, s, mid, b);
MergeSortAndcount(a, mid + 1, e, b);
merge(a, s, mid, e, b);
}
}
int main(){
cin>>n;
for(int i = 0; i < n; i++){
cin>>a[i];
}
MergeSortAndcount(a, 0, n-1, b);
cout<<ans<<endl;
return 0;
}
3.题目为给出图片
接下来给出题解代码:
#include<iostream>
#include<string.h>
using namespace std;
int a[10005];
void max1max2(int *a, int s, int e, int &max1, int &max2){
if(s == e){//当所给的数组只有一个数时,此时这个数是最大的,第二大的可以给很小的数
max1 = a[s];
max2 = -100005;
}else if(s+1 == e){//当只有俩个数时,直接判断出来即可,然后给函数的引用
max1 = max(a[s], a[e]);
max2 = min(a[s], a[e]);
}else{//如果更多数时,则可以才用将数组分段去求,即求左边的最大和次打,再求出右边的最大和次大,最后比较这四个数,给出最大和次大的值即可
int mid = s + (e - s)/2;//分段
int smax1, smax2;
max1max2(a, s, mid, smax1, smax2);//这里是求左边最大和次大
int emax1, emax2;
max1max2(a, mid+1, e, emax1, emax2);//这里是求右边的最大和次大
if(smax1 > emax1){//最后,这里判断赋值即可
max1 = smax1;
max2 = max(smax2, emax1);
}else{
max1 = emax1;
max2 = max(smax1, emax2);
}
}
}
int main(){
int n;
cin>>n;
for(int i = 0; i < n; i++){
cin>>a[i];
}
int max1, max2;
max1max2(a, 0, n-1, max1, max2);//因为要把最大的数和次大的数返回,所以这个函数用了引用给外部的值
cout<<max1<<" "<<max2<<endl;
return 0;
}
4.最大子段和
求给出数段中的最大的子段和,这里用的是分治法去求的最大子段和,当然也可以用动态规划去解决,且动态规划的复杂度更小,接下来给出解题思路:
接下来给出代码:
#include<iostream>
#include<string.h>
using namespace std;
typedef long long ll;
int n, a[2000200];
ll Max(ll x, ll y){
return x>y?x:y;
}
int maxans(int *p, int s, int e){
if(s == e){
if(p[s] < 0){
return 0;
}
return p[s];
}else{
ll sum = 0;
int mid = s + (e - s)/2;
// int sumleft = maxans(a, s, mid);//第一种情况,最大和在左边
// int sumright = maxans(a, mid+1, e);//第二种情况,最大和在右边
ll s1 = -19260817, sleft = 0;//接下来是第三种情况,最大和在左右俩边都有
for(int i= mid; i >= s; i--){//这里是用mid作为开始,是因为这里是要从这里开始取大的值
sleft = sleft + p[i];//这里让s1= -19260817的原因和下面的原因一样
if(sleft > s1){
s1 = sleft;
}
}
ll s2 = -19260817, sright = 0;//让s2等于负数且比题目给的绝对值小,因为第一个比较的数可能是负数
for(int i = mid+1; i <= e; i++){
sright = sright + p[i];
if(sright > s2){
s2 = sright;
}
}
return Max(Max(maxans(p, s, mid), maxans(p, mid+1, e)), s1+s2);
}
}
int main(){
cin>>n;
for(int i = 1; i <= n; i++){
cin>>a[i];
}
ll maxn = maxans(a, 1, n);
cout<<maxn<<endl;
return 0;
}
5.棋盘覆盖问题:地毯填补
解题思路:一般公主的位置(特殊位置)在正方形四部分的哪个位置,第一铺的中间的所选的地毯在那个位置要没有地毯.才能不覆盖掉特殊位置.
解题代码为:
#include<iostream>
#include<string.h>
using namespace std;
int k, map[1050][1050];
int x, y, zx, zy;//xy为公主的坐标或特殊点的坐标(即已经被覆盖了地毯的点),zx,zy为小正方形的左上角坐标
void pudi(int n, int x, int y, int zx, int zy){
if(n == 1){//当大正方形的边长都为1时,直接结束递归调用
return;
}//这里是把大正方形分成四个小正方形,判断zx,zy的点在哪个小正方形,然后哪个小正方形为空,不铺毯子
int mid = n / 2;
if(zx+mid > x && zy+mid > y){//特殊点在左上角
cout<<zx+mid<<" "<<zy+mid<<" "<<1<<endl;//先在中间填一个地毯,这块地毯把正方形分成了四块更小的正方形
pudi(mid, x, y, zx, zy);//递归填补左上角小正方形地毯
pudi(mid, zx+mid-1, zy+mid, zx, zy+mid);//递归填补右上角小正方形地毯
pudi(mid, zx+mid, zy+mid-1, zx+mid, zy);//递归填补左下角小正方形地毯
pudi(mid, zx+mid, zy+mid, zx+mid, zy+mid);//递归填补右下角小正方形地毯
}else if(zx+mid > x){//特殊点在右上角
cout<<zx+mid<<" "<<zy+mid-1<<" "<<2<<endl;
pudi(mid, zx+mid-1, zy+mid-1, zx, zy);
pudi(mid, x, y, zx, zy+mid);
pudi(mid, zx+mid, zy+mid-1, zx+mid, zy);
pudi(mid, zx+mid, zy+mid, zx+mid, zy+mid);
}else if(zy+mid > y){//特殊点在左下角
cout<<zx+mid-1<<" "<<zy+mid<<" "<<3<<endl;
pudi(mid, zx+mid-1, zy+mid-1, zx, zy);
pudi(mid, zx+mid-1, zy+mid, zx, zy+mid);
pudi(mid, x, y, zx+mid, zy);
pudi(mid, zx+mid, zy+mid, zx+mid, zy+mid);
}else{//特殊点在右下角
cout<<zx+mid-1<<" "<<zy+mid-1<<" "<<4<<endl;
pudi(mid, zx+mid-1, zy+mid-1, zx, zy);
pudi(mid, zx+mid-1, zy+mid, zx, zy+mid);
pudi(mid, zx+mid, zy+mid-1, zx+mid, zy);
pudi(mid, x, y, zx+mid, zy+mid);
}
}
int main(){
cin>>k;
cin>>x>>y;
int s = 1 << k;//刚开始的特殊点是公主的坐标,不能覆盖地毯的点
pudi(s, x, y, 1, 1);
return 0;
}
也有办法把那些繁琐的代码简化,如下:
6.接下来是一道考虑较多的分治题目:MooFest G
这里要考虑到每对点的情况,所以比较复杂:
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
long long n;
long long ans = 0;
struct Node{
int vi;
int xi;
}p[20010];
bool cmp1(Node a, Node b){//以v为第一关键字排序
if(a.vi != b.vi){
return a.vi < b.vi;
}
return a.xi < b.xi;
}
bool cmp2(Node a, Node b){//以x为第一关键字排序(其实这个排序不需要第二关键字)
if(a.xi != b.xi){
return a.xi < b.xi;
}
return a.vi < b.vi;
}
void sumant(int s, int e){
if(s == e){
return;
}
int mid = s + (e - s)/2, l = s;
long long s1 = 0, s2 = 0;
sumant(s, mid);//计算前半部分的
sumant(mid+1, e);//计算后半部分的
//sort(p+s, p+mid+1, cmp2);//将左边按x的大小排序
//sort(p+mid+1, p+e+1, cmp2);//将右边按x的大小排序
for(int i = s; i <= mid; i++){
s1 = s1 + p[i].xi;//这里是计算总的x和,之后用s1去存比p[i]的x大的
}
for(int i = mid+1; i <= e; i++){
while(l <= mid && p[l].xi < p[i].xi){
s2 += p[l].xi;//存小于当前坐标x的和
s1 -= p[l].xi;//存大于当前坐标x的和
l++;
}
int cnt1 = l - s, cnt2 = mid - l + 1;//cnt1去存小于当前坐标的个数,cnt2去存大于当前坐标的个数
ans += p[i].vi*(cnt1*p[i].xi - s2 + s1 - cnt2*p[i].xi);//这里是去计算每头牛产生的对其他牛的声音,然后计算全部的和
}
sort(p+s, p+e+1, cmp2);//在分完俩份,这里刚开始递归到这里的时侯是只有俩个数,去排序
}
int main(){
cin>>n;
for(int i = 1; i <= n; i++){
cin>>p[i].vi>>p[i].xi;
}
sort(p+1, p+n+1, cmp1);
sumant(1, n);
cout<<ans<<endl;
return 0;
}