1.最长连续子序列Largest Sum Contiguous Subarray (O(nlog(n)))
#include <bits/stdc++.h>
using namespace std;
long long A[100002];
int n;
int T;
long long nlogn(long long *A, int x, int y){
long long v, L, R, max;
int m;
if( y-x==1 ) return A[x];
m = x + (y-x)/2;
// 两边找,找最大的 存为max
max = nlogn(A, x, m); // [x, mid)的最大子序列和
v = nlogn(A, m, y); // [mid, y)的最大子序列和
if(v>max) max = v;
// [x, mid)最大连续和
v = 0;
L = A[m-1];
for(int i=m-1; i>=x; i--){
v += A[i];
if( v>L ) L = v;
}
// [mid, y)最大连续和
v = 0;
R = A[m];
for(int i=m; i<y; i++){
v += A[i];
if( v>R ) R = v;
}
// L+R一定是包含中点mid的和
// L+R是[x,y)最大连续和 看会不会比两边的大
if( L+R > max ) max = L+R;
return max;
}
int main(){
scanf("%d", &T);
for(int j=0; j<T; j++){
scanf("%d", &n);
for(int i=0; i<n; i++){
scanf("%lld", &A[i]);
}
printf("%lld\n", nlogn(A, 0, n));
}
return 0;
}
DP(对不起我真的理解不了分治)
1.明确dp数组含义:dp[i] 表示以a[i]结尾的最大子段和
【方法一】
2.初始化:每个元素最开始都是以自己开头以自己结尾,所以dp[i] = a[i]
3.状态迁移:遍历数组a[],对于每一个数,判断加不加入连续子段,判断条件是添加该数会不会让字段和相较于原dp[]增加。
【方法二】
2.初始化:dp[0] = a[0]其余全是0
3.状态迁移:遍历数组a[],对于每一个数,判断加不加入连续子段,判断条件是dp[i-1]如果是小于0的,说明a[i]加入子串相较于以a[i]为起点的新子串,由于前者为负数所以一定相较后者小,所以选择新子串(以a[i]为起点)。
memset(dp, 0, sizeof(dp)); dp[0] = a[0]; for(int i=1; i<n; i++){ if( dp[i-1]+a[i]>=0 ){ dp[i] = dp[i-1]+a[i]; } else{ dp[i] = a[i]; } }
#include <bits/stdc++.h>
using namespace std;
long long dp[100005];
long long a[100005];
int t,n;
int main(){
scanf("%d", &t);
while(t--){
scanf("%d", &n);
for(int i=0; i<n; i++){
scanf("%lld", &a[i]);
}
for(int i=0; i<n; i++){
dp[i] = a[i];
}
for(int i=1; i<n; i++){
if( dp[i-1]+a[i]>=dp[i] ){
dp[i] = dp[i-1]+a[i];
}
else{
dp[i] = a[i];
}
}
long long max = -1e18;
for(int i=0; i<n; i++){
if( dp[i]>max ){
max = dp[i];
}
}
printf("%lld\n", max);
}
}
2.找第k大的元素O(n)
本质是快排,先看快排代码
input的序列是乱序,排序时间复杂度会超过On,只能快排前k个数。代码找的是中位数,即n/2。
#include <bits/stdc++.h>
using namespace std;
int a[2000002];
int t,n;
int partition(int left, int right){
int pivot = a[right];
int flag = left - 1;
while( left<right ){
if( a[left] < pivot ){ // 小于pivot的放在枢纽元素的左侧
flag++;
swap(a[flag], a[left]);
}
left++;
}
swap(a[flag+1], a[right]);
return flag+1;
}
int quickSelect(int left, int right, int k){
if( left==right ) return a[left];
int pivotIndex = partition(left, right);
if( k==pivotIndex ){
return a[pivotIndex];
}
else if( k<pivotIndex ){
return quickSelect(left, pivotIndex-1, k);
}
else{
return quickSelect(pivotIndex+1, right, k);
}
}
int main(){
scanf("%d", &t);
for(int i=0; i<t; i++){
scanf("%d", &n);
for(int j=0; j<n; j++){
scanf("%d", &a[j]);
}
printf("%d\n", quickSelect(0, n-1, n/2));
}
}
3.最近点对the closest pair
#include <bits/stdc++.h>
using namespace std;
struct Point{
double x;
double y;
};
Point a[1000001], b[1000001];
int n;
// 按照先x后y的顺序从小到大排序
bool cmpPoint(Point a, Point b){
if( a.x == b.x ){
return a.y < b.y;
}
else{
return a.x < b.x;
}
}
// 按照y坐标从小到大排序
bool cmpY(Point a, Point b){
return a.y < b.y;
}
double dis(Point a, Point b){
return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
double nlogn(int left, int right){
if( left==right ) // 同点距离无穷大
return numeric_limits<double>::max();
if( left+1==right ) // 临点距离为直线距离
return dis(a[left], a[right]);
int mid = (left+right)>>1;
double mindis = nlogn(left, mid);
double v = nlogn(mid+1, right);
if( v < mindis )
mindis = v;
// 找出x轴上[中线-min, 中线+min]之间的点
int j=0;
for(int i=left; i<=right; i++){
if( fabs(a[i].x-a[mid].x) < mindis ){
b[j] = a[i];
j++;
}
}
// 按y排序
sort(b, b+j, cmpY);
// 如果中间区域纵坐标之差小于min, 更新min的值
for(int i=0; i<j; i++){
for(int k=i+1; k<j&&b[k].y-b[i].y<mindis; k++){
v = dis(b[i], b[k]);
if( v<mindis )
mindis = v;
}
}
return mindis;
}
int main(){
while(scanf("%d", &n)!=EOF){
for(int i=0; i<n; i++){
scanf("%lf %lf", &a[i].x, &a[i].y);
}
sort(a, a+n, cmpPoint);
int solider = floor(nlogn(0, n-1));
printf("%d\n", solider);
}
// scanf("%d", &n);
// for(int i=0; i<n; i++){
// scanf("%lf %lf", &a[i].x, &a[i].y);
// }
// sort(a, a+n, cmpPoint);
// printf("%.4lf\n", nlogn(0, n-1));
}
4.归并排序