算法入门经典第八章书上例题与代码(排序从前到后)

7 篇文章 0 订阅
归并排序
#include<iostream>
using namespace std;

int A[5];
int T[10];
void marge_sort(int *A,int x,int y,int *T)
{
int p,q,i,m;
if(y-x>1)//当y-x>1的时候即有两个元素的时候就不在划分了
{
m=x+(y-x)/2;//m这样定义,正好是中点
p=x;q=m;i=x;
marge_sort(A,x,m,T);//对左半部分递归
marge_sort(A,m,y,T);//对右半部分递归

while(p<m||q<y)
{
if(q>=y||(p<m&&A[p]<=A[q]))T[i++]=A[p++];//比较大小,且如果一方已经完全放进去的时候将剩下的放进去
else 
T[i++]=A[q++];
}
for(int i=x;i<y;i++)
          A[i]=T[i];//将数据存放回A数组
}
}

int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>A[i];
marge_sort(A,0,n,T);
for(int i=0;i<n;i++)
{
cout<<A[i]<<" ";
}
return 0;
}
#include<stdio.h>
void fun(int *a,int *b)
{
int t=*a;
*a=*b;
*b=t;
}
int main()
{
int a=0,b=1;
fun(&a,&b);
printf("%d %d",a,b);
return 0;
}
#include<stdio.h>
void fun(int a,int b)
{
int t=a;
a=b;
b=t;
}
int main()
{
int a=0,b=1;
fun(a,b);
printf("%d %d",a,b);
return 0;
}
//#include<stdio.h>
//void fun(char *s,int *t)
//{
// int i,n;
// n=0;
// for(i=0;s[i]!='\0';i++)
// {
// if(s[i]>'0'&&s[i]<'9')
// n++;
// *t=n;
// }
//}
//int main()
//{
// char s[80]="dfqsfa2334sdad";
// int t;
// fun(s,&t);
// printf("%d",t);
// return 0;

//}

快速排序:设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)
作为关键数据,然后将所有比它小的数都放到它前面,然后将这个数和比他小的数的后面一个值交换,正好得到所有比它大的数都放到它后面,这个过程称为一趟快速排序。
快速排序每次将待排序数组分为两个部分,在理想状况下,每一次都将待排序数组划分成等长两个部分,则需要logn次划分。
而在最坏情况下,即数组已经有序或大致有序的情况下,每次划分只能减少一个元素,快速排序将不幸退化为冒泡排序,所以快速排序时间复杂度下界为O(nlogn),最坏情况为O(n^2)。在实际应用中,快速排序的平均时间复杂度为O(nlogn)。
快速排序在对序列的操作过程中只需花费常数级的空间。空间复杂度S(1)。
//
//#include<iostream>
//using namespace std;
从小到大
//int partition(int a[],int p,int r){
// int x=a[r];//通常,拿最后一个值,作为预期的中间值
// int middle=p;//记录“较小的一段数据”的最大下标。通常这个值在p和r的中间,故起名middle
// for(int j=p;j<r;j++){
// if(a[j]<x)//当这个数小于x时,放在前面。
// {
// if(j!=middle)//当这个数字正好等于middle的时候,就证明不用和middle换了,记得每次有一个小于x的middle+1
// {
// int temp=a[middle];
// a[middle]=a[j];
// a[j]=temp;
// }
// middle++;
// }
// }
// //最后将这个数和middle交换
// int temp=a[r];
// a[r]=a[middle];
// a[middle]=temp;
// return middle;
//}
//
//void QuickSort(int a[],int p,int r){
// if(p<r){
// int q=partition(a,p,r);//完成一次快速排序,
// //cout<<"q="<<q<<" ";
// QuickSort(a,p,q-1);//紧接着完成相对前一半的快速排序
// QuickSort(a,q+1,r);//紧接着完成相对后一半的快速排序
// }
//}
//
//int main(){
// int array[]={6,5,7,3,8,9};
// QuickSort(array,0,5);
// for(int i=0;i<=5;i++)
// cout<<array[i]<<" ";
// cout<<endl;
// return 0;
//}


棋盘覆盖问题 :
有一个 2^k*2^k 个方格棋盘,恰有一个方格是灰色的,其他为白色,
你的任务是用包含 3 个方格的 L 型骨牌覆盖所有白色方格。 
灰色方格不能被子覆盖, 且任意一个白色方格不能同 时被两个或更多骨牌覆盖。


//递归详解思路:首先,棋盘覆盖是不断的重复一个步骤,所以很适用与递归,首先应该有把棋盘不断分成四等分的意识。
//一次一次的划分,当棋盘只有四个格子的时候,这个时候如果特殊格子被找到,进入递归,此时size是1,所以直接跳出递归,然后又三个else,
//这三个else正好用同一个数字填充了四个格子中不是特殊方格的格子,比如如下期盼覆盖:
//0 2 3 3
//2 2 1 3
//4 1 1 5
//4 4 5 5
//这个棋盘递归的顺序是:首先进入递归,if判断特殊格子的地方,判断在左上方,然后进入下一个递归,
//此时t1++了一次了,也就是等于1,然后size/=2,size此时等于2
//再由if来判断特殊格子的地方,然后在if中又进入了递归,此时size等于1了,t1等于2
//再由if判断,再进入递归,但是size=1,跳出递归,继而程序向下走,碰到else就把其他的三个格子标记上t1,然后递归,
//之后递归返回到size等于2,t1=1的递归中,然后用if判断,给其他的点赋值,其余依次类推


//#include<iostream>
//using namespace std;
//const int N=11;
//int t=0;
//int A[N][N];
//void CB(int tr,int tc,int dr,int dc,int size)
//{
// if(size==1)return;//当划分为1个格子的时候,就返回这个递归
// int s,t1;
// t1=++t;
// s=size/2;
//
// if(dr<tr+s&&dc<tc+s)//特殊方格在左上角
// {
// CB(tr,tc,dr,dc,s);
// }
// else//如果不在左上角,那么将右下角标记为特殊方格
// {
// A[tr+s-1][tc+s-1]=t1;//划分成一个格子的时候,当那个格子不在其余这四个格子的地方的时候,会对这四个格子编号
// CB(tr,tc,tr+s-1,tc+s-1,s);
// }
//
// if(dr<tr+s&&dc>=tc+s)//特殊方格在右上角
// {
// CB(tr,tc+s,dr,dc,s);
// }
// else//如果不在右上角,那么将左下角标记为特殊方格
// {
// A[tr+s-1][tc+s]=t1;
// CB(tr,tc+s,tr+s-1,tc+s,s);
// }
// if(dr>=tr+s&&dc<tc+s)//特殊方格在左下角
// {
// CB(tr+s,tc,dr,dc,s);
// }
// else//如果不在左下角,那么将右上角标记为特殊方格
// {
// A[tr+s][tc+s-1]=t1;
// CB(tr+s,tc,tr+s,tc+s-1,s);
// }
// if(dr>=tr+s&&dc>=tc+s)//特殊方格在右下角
// {
// CB(tr+s,tc+s,dr,dc,s);
// }
// else//如果不在左上角,那么将右下角标记为特殊方格
// {
// A[tr+s][tc+s]=t1;
// CB(tr+s,tc+s,tr+s,tc+s,s);
// }
//}
//
//void display(int size)
//{
// for(int i=0;i<size;i++)
// {
// for(int j=0;j<size;j++)
// {
// cout<<A[i][j]<<" ";
// }
// cout<<endl;
// }
//}
//int main()
//{
// int size;
// cin>>size;
// int dr,dc;
// cin>>dr>>dc;
// CB(0,0,dr,dc,size);
// display(size);
// return 0;
//}

循环日程表问题:
有 n=2^k 个运动员要进行网球循环赛。需要设计比赛日程表。
每个选手必须与其他 n-1 个选手个比赛一次;每个选手一天只能赛一次;循环赛一共进行 n-1 天。
按此要求设计一张 比赛日程表,它有 n 行和 n-1 列,第 i 行第 j 列为第 i 个选手在第 j 天遇到的选手。
//
递归算法分析:首先,将表格不断的四分,直到左上角是2*2的格子为止,然后给左上角赋值,
之后给给四个格子的其他三个格子赋值其他三个格子也是对角是相等的,而右边的格子等于左边的格子对应的下标加上格子列数的一半
其他格子的赋值是相同的,但是要明白递归总是从局部往大块实现的
//#include <iostream> 
//using namespace std; 
//int table[100][100]; 
//void Creattable(int r1,int c1,int r2,int c2,int size)//r1,c1表示左上角第一个格的行和列,r2,c2表示划分的左上角的右下角的格子的行和列
//{ 
// int i,j; 
// int halfsize=size/2; 
// if(size>1) //递归创建左上角的赛程表 (别忘了这个地方的size在递归中就是halfsize了)
// Creattable(0,0,halfsize,halfsize,halfsize); 
// else table[0][0]=1; 
// for(i=0;i<size;i++) //这个时候的size是递归中的size,别搞错了这两个for循环也只是在对左上角进行赋值操作
// for(j=0;j<size;j++) 
//
// if(i<halfsize && (j>=halfsize && j<size)) //右上角的赛程是左上角的赛程加上 halfsize
// table[i][j]=table[i][j-halfsize]+halfsize; 
// if((i>=halfsize && i<size) && j<halfsize) //左下角的赛程和右上角的赛程相同 
// table[i][j]=table[i-halfsize][j+halfsize]; 
// if((i>=halfsize && i<size) && (j>=halfsize && j<size)) //右下角的赛程和左上角的赛程相同 
// table[i][j]=table[i-halfsize][j-halfsize]; 
//
//} 
//int main()
//{
// int i,j,k,n=1; 
// cin>>k; //输入 k 
// for(i=1;i<=k;i++) 
// n=n*2; //计算 n=2^k
// Creattable(0,0,n,n,n); 
// for(i=0;i<n;i++) 
//
// cout<<"运动员"<<table[i][0]<<"的每日赛程: "; 
// for(j=1;j<n;j++) 
// cout<<table[i][j]<<" "; 
// cout<<endl; 
//
// return 0; 
//}


8.3.5 最大值最小化 
把一个包含 n 个正整数的序列划分成 m 个连续的子序列(每个正整数恰好属于一个序 列) 。
设 i 个序列的各数之和为 S(i),你的任务是让所有 S(i)的最大值尽量小。
例如序列 1 2 3 2 5 4 划分成 3 个序列的最优方案为 1 2 3|2 5|4,
其中 S(1)、S(2)、S(3)分别为 6、7、 6 4,最大值为 7;如果划分成 1 2|3 2|5 4,最大值为 9,
不如刚才好。n≤10 。所有数之和 9 不超过 10 。 
【分析】 “最大值尽量小”是一种很常见的优化目标。
对于这个问题:能否把输入序列划分成 m 个连续的子序列, 使得所有 S(i)均不超过 x?此问题的答案用 P(x)表示, 
则让 P(x)为真的最 小 x 就是原题的答案。对 P(x)计算,每次尽量往右划分即可。 
二分最小值 x,把优化问题转化为判定问题 P(x)。设所有数之和为 M,则二分数为 O(logM),
计算 P(x)的时间复杂度为 O(n)(从左到右扫描一次即可),因此总时间复杂度为 O(nlogM)。
//#include <iostream> 
//#include <ctime> 
//using namespace std; 
//#define N 10 
//#define INF 1000 
//int juge(int a[],int halfnum,int k) 
//{ 
// int i; 
// int seg=0; 
// int sum=0; 
// for(i=0;i<N;i++) 
//
// sum+=a[i]; 
// if(sum>halfnum)//如果前一段已经大于整个数组总和的一半了,就再起一段
//
// //从左到右将数组元素之和与 halfnum 比较, 如是大于则再起一段, 最后看段的大小 
// sum=a[i]; 
// seg++; 
// }
//} 
// if(seg>=k) //若是段超过 3,则必然不和条件 
// return 0; 
// else return 1; 
//} 
//int value(int a[],int min,int allsum,int segment) //分治法求解 segment是段的意思
//{ 
// if(min>allsum) 
// return allsum+1; 
// else 
//
// int halfnum=(min+allsum)/2;//halfsum是最大值最小值之和的一半
// //一下if和else不是很懂不知道为什么是这样递归的
// if(juge(a,halfnum,segment)==1) //如果试验数 halfnum 符合要求, 递归到前一半 
// return value(a,min,halfnum-1,segment); 
// else //如果试验数 halfnum 不符合要求,递归到后一半 
// return value(a,halfnum+1,allsum,segment); 
// }
//} 
//int main() 
//{ 
// //srand((unsigned)time(NULL)); //随机得到数组的值
// //int a[N]; 
// //for(int ifor=0;ifor<N;ifor++) 
// // a[ifor]=rand()%20; 
//
// int a[N]={9,19,15,13,13,9,14,1,1,7}; 
// for(int ifor=0;ifor<N;ifor++) 
// cout<<a[ifor]<<" ";
// int m=3;//分成m段
// //求出队列中所有数的和 allsum,还要求出当中最小的数 min 
// int min=INF,allsum=0; 
// for(int i=0;i<N && a[i]!=' ';i++) 
//
// allsum+=a[i]; 
// if(a[i]<min) 
// min=a[i]; 
//
// cout<<endl; 
// int tem=value(a,min,allsum,m); //调用 value 函数求值 
// cout<<tem<<endl; 
// return 0; 
//}


8.4.1 最优装载问题 
给出 n 个物体, 第 i 个物体重量为 wi。选择尽量多的物体, 使得总重量不超过 C。 
【分析】 
由于目标是物体的“数量”尽量多,所以装重的没有装轻的划算。
只需把所有物体按重 量从小到大排序,依次选择每个物体,直到装不下为止。
这就是一种贪心法,因为每次都是 选择能装下的最轻的物体,是一种“只顾眼前”的策略,
这样的策略却能保证得到最优解。 
//#include <stdio.h> 
//#define N 100 
//int x[N],w[N],t[N]; 
//void Sort(int w[],int t[],int n) 
//{ 
// //将重量数组 w 从小到大排序,数组 t[i]表示数组 w 第 i 小的元素在数组 w 中的下标 
// int i,j,temp,k,w1[N]; 
// for(i=1;i<=n;i++) //设辅助数组 w1 的作用是用于排序,原数组 w 元素不移动 
// w1[i]=w[i]; 
// for(i=1;i<=n;i++) //初始化数组 t,记下原重量数组 w 中每个元素的位置 
// t[i]=i; 
// for(i=1;i<n;i++){ 
// //用选择排序法进行排序
// k=i; 
// for(j=i+1;j<=n;j++) 
// if(w1[k]>w1[j]) 
// k=j; 
// if(k!=j)
//
// //将数组 w 中的元素进行交换,同时数组 t 中元素也交换 
// //排序完成后, t[i]应为重量第 i 小的物体在原重量数组 w 所处的位置 (下标) 
// temp=t[i]; 
// t[i]=t[k]; 
// t[k]=temp; 
// temp=w1[i]; 
// w1[i]=w[k]; 
// w1[k]=temp; 
// }
//
// }
//} 
//void Loading(int x[],int w[], int c,int n) 
//{ 
// int i;
// Sort(w,t,n); //对数组 w 进行排序,数组 t 记下数组 w 中元素的大小关系 
// for(i=1;i<=n;i++) x[i]=0; //初始化数组x 
// for(i=1;i<=n && w[t[i]]<=c;i++) 
//
// //贪心选择 
// //数组 x 表示是否选择物体的状态,x[t[i]]=1 表示选择重量第 i 小的物体
// x[t[i]]=1; 
// c-=w[t[i]];
//
//} 
//int main(){ 
// int i,n,c; 
// int max=0;
// while(scanf("%d%d",&n,&c)!=EOF) //输入物体数 n,总重量 c 
//
// for(i=1;i<=n;i++) //输入重量数组 w 的元素 
// scanf("%d",&w[i]); 
// Loading(x,w,c,n); 
// printf("选择的物体为:\n"); 
// for(i=1;i<=n;i++) 
//
// //当 x[i]=1 选择数组 w 中第 i 个物体的 w[i](重量) ;当 x[i]=0 不选择 
// printf("%3d",x[i]); max+=w[i]*x[i]; //计算最大总重量 max 
//
// printf("\n");
// printf("选择物体的最大总重量为:%d\n",max);
//
//} 


//8.4.2 部分背包问题 
//有 n 个物体, 第 i 个物体的重量为 wi, 价值为 vi, 在总重量不超过 C 的情况下让总价值 尽量高。
//每一个物体可以只取走一部分,价值和重量按比例计算。 
//【分析】 本题在上一题的基础上增加了价值, 所以不能简单的像上题那样先拿轻的 (轻的可能价 值也小) ,
//也不能先拿价值大的(可能它特别重) ,而应该综合考虑两个因素。一种直观的贪心策略产生:
//优先拿“价值/重量比”最大的,直到重量和正好为 C。由于每个物体可以只拿一部分,
//因此一定可以让总重量恰好为 C(或者全部拿走重量也不 足 C) ,而且除了最后一个以外,
//所有的物体要么不拿,要么拿走全部。


//也可以用结构体的双变量排序:
/*struct node{  
    int v;//价值  
    int w;//重量
}s[100010];  
int cmp(node x,node y){  
    if(x.v==y.v)   //当两个值相等的 
        return x.w<y.w;  
    return x.v>y.v;  
} */ 
//#include <stdio.h>
//#include <iostream> 
//#include<stdlib.h> 
//#define MAXSIZE 100 //假设物体总数 
//#define M 15 //背包的载荷能力 
//using namespace std; 
算法核心,贪心算法 
//void GREEDY(float w[], float x[], int sortResult[], int n) 
//{
// float c = M; 
// int i = 0; 
// int temp = 0; 
// for (i = 0; i < n; i++) //准备输出结果 
// x[i] = 0; 
// for (i = 0; i < n; i++) 
//
// for(int j=0;j<n;j++) 
// if(sortResult[j]==i+1) 
//
// temp = j;//得到取物体的顺序 
// break; 
//
// if (w[temp] > c) 
// {
// break; 
//
// x[temp] = 1;//若合适则取出 
// c -= w[temp];//将容量相应的改变 
//
// if (i <= n)//使背包充满 
// x[temp] = c / w[temp]; //取某件物品的一部分 
// return; 
//} 
//void sort(float x[], int sortResult[], int n) 
//{ 
// int i = 0, j = 0; 
// int index = 0, k = 0; 
// for (i = 0; i < n; i++)//对映射数组赋初值 0 
// sortResult[i] = 0; 
// for (i = 0; i < n; i++) 
//
// float temp = 0; 
// index = i; //找到性价比最高的商品,并保存下标 
// for (j = 0; j < n; j++) 
//
// if ((temp < x[j]) && (sortResult[j] == 0)) 
//
// temp = x[j]; 
// index = j; 
//
//
// //对 w[i]作标记 
// if (sortResult[index] == 0)
// sortResult[index] = ++k; 
//
// cout<<"映射数组 sortResult:"<<endl; 
// for (i = 0; i < n; i++) 
// cout<<sortResult[i]<<" "; 
// return; 
//} 
得到本算法的所有输入信息 
//void getData(float p[], float w[], int *n)
//{ 
// int i = 0; 
// printf("please input the total count of object: "); 
// scanf("%d",n); 
// printf("Please input array of p :\n"); 
// for (i = 0; i < (*n); i++) 
// scanf("%f", &p[i]); 
// printf("Now please input array of w :\n"); 
// for (i = 0; i < (*n); i++) 
// scanf("%f", &w[i]); 
// return; 
//} 
//void output(float x[], int n) 
//{ 
// int i; 
// printf("\n\nafter arithmetic data: advise method\n"); 
// for (i = 0; i < n; i++) 
// printf("x[%d]\t", i); 
// printf("\n"); 
// for (i = 0; i < n; i++) 
// printf("%2.3f\t", x[i]); 
// return; 
//} 
//int main() 
//{ 
// float p[MAXSIZE], w[MAXSIZE], x[MAXSIZE]; 
// int i = 0, n = 0;
// int sortResult[MAXSIZE]; 
// getData(p, w, &n); //获取数据 
// for (i = 0; i < n; i++) 
// x[i] = p[i] / w[i]; 
// //得到每件物品的单位重量的价值 
// sort(x, sortResult, n); 
// //得到映射数组,数组中按照物品单位重量的 价值从大到小的顺序做了标记,方便取物品 
// GREEDY(w,x,sortResult,n); //按照映射数组标记的顺序取物品,和总重量比较 
// output(x, n); 
// return 0;
//} 


//8.4.3 乘船问题有个人个人重量为wi。每艘船的载重量均为C, 最多乘两个人。

//用最少的船装载所有人。

 

//【分析】考虑最轻的人i,他应该和谁一起坐呢?如果和每个人都无法一起坐同一艘船,

//则唯一的方案就是每人坐一艘船。否则选择能和一起坐船的人中最重的一个j

//这样的方法是贪心的,因此它只是让眼前船的浪费尽量少。

//这个贪心策略是对的,可以用反证法说明。

//假设这样做不是最好的,考虑最好方案中是什么样的。

//情况1不和任何一个人坐同一艘船,那么可以把拉过来和他一起坐,

//总船数不会增加(且可能会减少!,并且符合刚才的贪心策略。)情况

//2和另外一人同船,由贪心策略,轻。把交换后原来所在的

//船仍然不会超重(因为轻),而所在的船也不会超重(由贪心法过程),

//因此所得到的新解不会更差,且符合贪心策略。综上所述,虽然可能不采取贪心策略也能得到最优解,

//但是只考虑贪心策略肯定不会丢失最优解。贪心法往往容易实现。在本题中,

//只需每次寻找最小值和能与它同船的最大值配对即可。

 

#include <iostream> 

#include <ctime> 

#include <algorithm> 

using namespace std; 

#define N 20 //人数

#define C 120 //船的承重

static int bcount=0; 

void boat_num(int weight[],int left,int right) 

int first,j; 

for(int i=0;i<=right;i++ ) 

first=weight[i]; //第一个人的重量

for(j=i+1;j<=right;j++) 

if(weight[j]>C-first) 

//如果第个人的重量加上第一个人的重量大于C,那么第个人和他后面的人都必须一个人一只船

cout<<"从第"<<j<<""<<right<<"的人需要独自乘船"

bcount+=right-j+1; //计算一个人一只船的人数

cout<<bcount<<endl; 

break

}

 

//j-1 个人和第一个人的重量之和是可承受范围的最大值,符合贪心算法

cout<<"" <<i<<"个人和第"<<j-1<<"个人同船"

bcount++; 

cout<<bcount<<endl; 

right=j-2; //right 置为j-2(因为本身应该是j-1,但是for循环的时候j++多加了一个,所以是j-2),进行下次迭代。

int main() 

int weight[N]; 

srand((unsigned)time(NULL)); 

//srand((unsigned)time(NULL));

for(int i=0;i<N;i++) 

weight[i]=40+rand()%60; 

sort(weight,weight+N); //将人的重量从小到大排好序

for(int i=0;i<N;i++) 

{

if(i>0 && 0==i%10)

cout<<endl;

cout<<weight[i]<<" ";

cout<<endl; 

boat_num(weight,0,N-1); //求需要的船数

cout<<"一共需要"<<bcount<<"条船"<<endl; 

return 0; 

 

选择不相交区间数轴上有个开区间(ai,bi),选择尽量多个区间,使得这些区间两两没有公共点。

【分析】首先明确一个问题:如果有两个区间x, y,区间完全包含y。那么显然选是不划算的,

因为最多只能选一个,选还不如选y,这样不仅区间数目不会减少,而且给其他区间留出了更多的位置。

这样,我们按照bi 从小到大的顺序给区间排序。

贪心策略是:一定要选第一个区间。现在区间已经排序成b1≤b2≤b3≤„,考虑a1 a2 的大小关系。

情况1a1>a2,区间包含区间1。前面已经讨论过,这种情况下一定不会选择区间2。不仅区间如此,

以后所有区间中只要有一个满足a1>aii都不要选。在今后的讨论中,我们不考虑这些区间。

情况2:排除了情况1,一定有a1≤a2≤a3≤„,如果区间和区间完全不相交,那么没有影响

(因此一定要选区间1)否则区间和区间最多只能选一个。注意到如果不选区间,

黑色部分其实是没有任何影响的(它不会挡住任何一个区间),区间的有效部分其实变成了灰色部分,

它被区间所包含!由刚才的结论,区间是不能选的。以此类推,不能因为选任何区间而放弃区间,

因此选择区间是明智的。

//

//

思路:先按照区间右端点从大到小排序第一个是肯定要选的再看后面的只要前一个区间的y

小于后面某个区间的x  则说明这个两个区间不相交,后面那区间可以选。

输入数据:

5

1 4

5 9

10 12

2 6

7 11

//

//

//

//#include<iostream>

//#include<algorithm>

//using namespace std;

//struct node

//{

//int x;//前区间点

//int y;//后区间点

//};

//int cmp(node a,node b)

//{

// return a.y<b.y;

//}

//

//int main()

//{

// int i,n,count,b;

// node q[100];

// while(scanf("%d",&n),n)

// {

//  for(i=0;i<n;i++)

//   scanf("%d%d",&q[i].x,&q[i].y);

//  sort(q,q+n,cmp); //按照y的大小给q从小到大排序

// 

//  b=q[0].y;count=1;

// // printf("  q[i].x %d  q[i].y  %d\n",q[0].x,q[0].y);

//  for(i=1;i<n;i++)

//  {

//   if(q[i].x>=b)

//   {

//    b=q[i].y;

//    count++;

//   // printf("  q[i].x %d  q[i].y  %d\n",q[i].x,q[i].y);

//   }

//  }

//  printf("%d\n",count);

// }

// return 0;

//}

//

8.4.5 区间选点问题

数轴上有个闭区间[ai,bi]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)。

【分析】如果区间内已经有一个点被取到,我们称此区间已经被满足。受到上一题的启发,

我们先讨论区间包含的情况。由于小区间被满足时大区间一定也被满足。所以在区间包含的情况下,

大区间不需要考虑。把所有区间按从小到大排序(相同时从大到小排序),

则如果出现区间包含的情况,小区间一定排在前面。第一个区间应该取哪一个点?

贪心策略是:取最后一个点,根据刚才的讨论,所有需要考虑的区间的也是递增的,可以把它画成上图的形式,

如果第一个区间不取最后的一个,而是取中间的,如灰色点,那么把它移动到最后一个点后,

被满足的区间增加了,而且原先满足的区间现在一定被满足。不难看出,这样的贪心策略是正确的。

思路:按照上面的分析可得,对区间按照b从小到大,b相同时a从大到小排序,

先去第一个区间的最后一个点,然后把有点的区间排除,再从当前最前区间选择最后一个点,一次类推。

输入

5

1 4

5 9

10 12

2 6

7 11

//#include<iostream>

//#include<algorithm>

//using namespace std;

//struct node

//{

//int x;

//int y;

//};

//int cmp(node a,node b)

//{

//if(a.y==b.y)return a.x>b.x;

//return a.y<b.y;

//}

//int main()

//{

//node qujian[100];

//int n;

//cin>>n;

//for(int i=0;i<n;i++)

//{

//cin>>qujian[i].x>>qujian[i].y;

//}

//sort(qujian,qujian+n,cmp);

//

//int b=qujian[0].y;

//int count=1;

//for(int i=1;i<n;i++)

//{

//if(qujian[i].x>b)

//{

//b=qujian[i].y;

//count++;

//}

//}

//cout<<count;

//return 0;

//}

8.4.6 区间覆盖问题

数轴上有个闭区间[ai,bi],选择尽量少的区间覆盖一条指定线段[s,t]

【分析】本题的突破口仍然是区间包含和排序扫描,不过先要进行一次预处理。

每个区间在[s,t] 外的部分都应该预先被切掉,因为它们的存在是毫无意义的。

在预处理后,在相互包含的情况下,小区间显然不应该考虑。把各区间按照从小到大排序。

如果区间的起点不是s,无解(因为其他区间的起点更大,不可能覆盖到点),

否则选择起点在的最长区间。选择此区间后[ai,bi],新的起点应该设置为bi

并且忽略所有区间在bi 之前的部分,就像预处理一样。虽然贪心策略比上题复杂,

但是仍然只需要一次扫描,为当前有效起点(此前部分已被覆盖),则应该选择区间2

//

思路:输入区间和s,t之后对所有的区间进行预处理,首先按照a从小到大排序,在a相同的基础上从大到小排序,

然后将区间小于s的大于t的通通截断。一次选取最长的区间,然后把区间的最后一个点赋值给s变为下一次的起点。

//

输入数据:

5

1 4

5 9

10 12

2 6

7 11

//#include<iostream>

//#include<algorithm>

//using namespace std;

//struct node

//{

//int x;

//int y;

//};

//int cmp(node a,node b)

//{

//if(a.x==b.x)return a.y>b.y;

//return a.x<b.x;

//}

//int main()

//{

//int n;

//node qujian[100];

//cin>>n;

//for(int i=0;i<n;i++)

//{

//cin>>qujian[i].x>>qujian[i].y;

//}

//sort(qujian,qujian+n,cmp);//给区间排序

//int s,t;

//cin>>s>>t;

//if(qujian[0].x>s)//说明没有能覆盖的

//cout<<"输入的s不符合条件!";

//int count=0;

////对区间进行预处理

////for(int i=0;i<n;i++)

////{

////if(qujian[i].x<s)

////qujian[i].x=s;

////if(qujian[i].y>t)

////qujian[i].y=t;

////}

//int k=0;

//int j=0;

//while(t-s>0)

//{

//int s1=s;

//for(j=k+1;j<n;j++)

//{

//if(qujian[j].x<=s)

//{

//if(qujian[j].y>s1)

//{

//s1=qujian[j].y;

//}

//}

//else

//break;

//}

//s=s1;

//k=j-1;

//count++;

//cout<<""<<count<<"个区间选择"<<qujian[k].x<<" "<<qujian[k].y<<endl;

//}

//cout<<"需要的区间数是:"<<count<<endl;

//return 0;

//}

 

//#include <stdio.h> 

//int A[] = {8,7,6,5,4,3}; 

//int T[] = {0,0,0,0,0,0}; 

//void inverse_pair(int* A, int x, int y, int* cnt, int* T) 

//{ 

//if(y-x > 1)//至少有两个元素

//

//int m = x + (y-x)/2; //划分

//int p = x, q = m, i = x; 

//inverse_pair(A, x, m, cnt, T); //递归求解

//inverse_pair(A, m, y, cnt, T); //递归求解

//while(p < m || q < y) 

//

//if(q >= y || (p < m && A[p] <= A[q])) //从左半数组复制到临时空间

//T[i++] = A[p++]; 

//else 

//

////从右半数组复制到临时空间(因为复制右边的数进去的时候,左边的数肯定大于右边的数,这时就赋值)

//T[i++] = A[q++];

//*cnt += m-p; //计算逆序对数

//

//

//for(i = x; i < y; i++) 

//A[i] = T[i]; //从辅助空间复制到数组

//

//} 

//int main()

//{ 

//int i, cnt = 0;

//inverse_pair(A, 0, 6, &cnt, T); 

//printf("%d\n", cnt); 

//return 0; 

//}

 

//小的数:

//输入个整数和一个正整数(≤k≤n)输入这些整数从小到大排序后的第个,

//(例如,k=1 就是最小值)。n≤^7 

//算法分析:首先,对于求k小的数会第一眼想到用数组对整个数组排序之后求出下标为k-1的数,

//但是这个地方n实在是太大了,所以还是想到用合并排序来解决,因为在合并排序的时候可以适当的减枝

//比如说:当一个较小的二分的数组的长度大于k的话,那就只对这个数组进行排序就好了,而,当较大的数组中包含k的话,

//那就用k减去大数组部分的最小下标p,即:k-p-1

#include<iostream>

using namespace std;

int k;

int  partition(int *A,int l,int r)

{

int x=A[r];

int m=l;//m是表示l,r中间的数,现在先赋值为最小的下标

for(int j=l;j<r;j++)

{

if(A[j]<x)//如果A[j]是小于x的,那么就将这个值从数组的最左端开始赋值

{

if(m!=j)//middlej不等的时候,交换信息

{

int temp=A[j];

A[j]=A[m];

A[m]=temp;

}

m++;//middle向后移一位

}

}

//A[m]和最后一个数交换

int temp=A[m];

A[m]=A[r];

A[r]=temp;

return m;

}

int selectK(int *A,int l,int r,int k)

{

int q=partition(A,l,r);

cout<<"q="<<q<<endl;

int pos=q-l+1;//下标从开始,要判断k-1r-l的关系

if(l==r)return A[l];//如果只有这一个字符了就直接return

if(pos==k)return A[q];//如果正好这个数等于k,就直接返回这个数

else if(k<pos){

return selectK(A,l,q-1,k);//左部排序

}

else

{

return selectK(A,q+1,r,k-pos);//右部排序

}

return 0;

}

int main()

{

int A[10]={0,5,2,3,7};

    

cout<<selectK(A,0,4,3);//这个地方是,因为一共是,这是N-1;但这段代码并没有体现当数字很大的时候

return 0;

}

//

//#include <iostream> 

//#include <cmath> 

//using namespace std; 

//const int N = 100; 

//int Partition(int a [N], int low, int high) //快速排序中的一次划分

//{ 

//int i = low; 

//int j = high + 1; 

//int x = a[high];

//while(true){ 

//while(a[++i] < x); //满足a[i]小于xi继续向后便历

//while(a[--j] > x); //满足a[j]大于xi继续向前便历

//if( i>=j ) break; //当两个都while条件都不满足时,判断ij的大小,如果i已经超过了j表示有错误,跳出

//swap( a[i], a[j] ); //否则交换两个数在数组中的位置,

cout<<"a[i]="<<a[i]<<"a[j]"<<a[j]<<endl;

//

//} 

//a[low] = a[j]; //此时将小的数赋值给a[low]

cout<<"a[low]="<<a[low];

//a[j] = x; //而将a[j]x替换,完成交换。

以上可以按一下例子理解:3 0 5 7

得到a[i]=3;a[j]=0;所以交换和得:0 3 5 7

之后再交换a[low]a[j]即交换和得到2 3 5 7

//return j; 

//} 

//

//int  partition1(int *A,int l,int r)

//{

//int x=A[r];

//int m=l;//m是表示l,r中间的数,现在先赋值为最小的下标

//for(int j=l;j<r;j++)

//{

//if(A[j]<x)//如果A[j]是小于x的,那么就将这个值从数组的最左端开始赋值

//{

//if(m!=j)//middlej不等的时候,交换信息

//{

//int temp=A[j];

//A[j]=A[m];

//A[m]=temp;

//}

//m++;//middle向后移一位

//}

//}

////A[m]和最后一个数交换

//int temp=A[m];

//A[m]=A[r];

//A[r]=temp;

//

//return m;

//}

//int Select_k(int a[N], int low, int high, int k)

//{ 

对数组中low-high 部分排序

//int q = Partition(a,low,high); 

///*int q = partition1(a,low,high); */

cout<<"q="<<q<<" ";

//int pos = q-low+1; //下标从零开始,要判断k-1 high-low 的关系

//if(low==high) return a[low]; 

//if(k==pos) return a[q]; 

//else if(k < pos) return Select_k(a,low,q-1,k); //左部排序

//else return Select_k(a,q+1,high,k-pos); //右部排序

//return 0; 

//} 

//int main() 

//{ 

//int low,q,high,n,i,k,highes,a[N]; 

//while(cin>>n>>k)

//{ 

//for( i=0; i<n; i++) 

//cin>>a[i]; 

//cout<<""<<k<<"小数:"<<Select_k(a,0,n-1,k)<<endl; 

//} 

//return 0; 

//} 

 

//#include<iostream>

//#include<algorithm>

//using namespace std;

//int v[1000];

//int main()

//{

//int n,m,a,b;

//scanf("%d%d",&n,&m);

//for(int i=0;i<n;i++)

//scanf("%d",&v[i]);

//

//sort(v,v+n);

//for(int i=0;i<m;i++)

//{

//scanf("%d%d",&a,&b);

//cout<<"u="<<upper_bound(v,v+n,b)<<endl;

//cout<<"l="<<lower_bound(v,v+n,a)<<endl;

//cout<<"u-l="<<upper_bound(v,v+n,b)-lower_bound(v,v+n,a)<<endl;

//}

//return 0;

//}

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值