分治法:归并排序、快速排序、最大子段和 、最近点对问题
贪心法:多级调度问题、活动安排问题、背包问题
动态规划法:、最大子段和、近似串匹配、最优二叉树 、最长公共子序列、0-1背包问题、多段图最短路径
*******************************************************************
分治法
1、归并排序
// Note:Your choice is C++ IDE
//归并排序(分治法)
#include
using namespace std;
//合并两个有序子序列
void Merge( int r1[ ] , int r[ ], int s, int m, int t)
{
int i,j,k;
i=s; j=m+1; k=s;
while (i<=m && j<=t) {
if (r[i]<=r[j]) r1[k++]=r[i++];
else r1[k++]=r[j++];
}
if (i<=m)
while (i<=m)
r1[k++]=r[i++];
else
while (j<=t)
r1[k++]=r[j++];
for( i=s; i<=t; i++)
r[i]=r1[i]; //r[]:第二个数组是合并排序后的序列
}
//分治法归并排序
void MergeSort(int r[ ], int r1[ ], int s, int t) {
int m;
if (s==t) r1[s]=r[s];
else {
m=(s+t)/2;
MergeSort(r, r1, s, m); //归并排序前半个子序列
MergeSort(r, r1, m+1, t); //归并排序后半个子序列
Merge(r, r1, s, m, t); //合并两个已排序的子序列
}
}
int main()
{
int r[256],r1[256];
int t;
cin>>t;
for(int i=1; i<=t; i++)
cin>>r[i];
int s=1;
MergeSort( r, r1, s, t) ;
for( i=1; i<=t; i++)
cout< return 0; } =================================== 2、快速排序 #include void Swap(int a[], int m, int n){ int temp; temp= a[m]; a[m]= a[n]; a[n] =temp; } int Partition(int r[ ], int first, int end) { int i=first ,j=end; //初始化 while (i { while (i if (i Swap(r,i,j); //将较小记录交换到前面 i++; } while (i if (i Swap(r,i,j); //将较大记录交换到后面 j--; } } return i; // i为轴值记录的最终位置 } void QuickSort(int r[ ], int first, int end) { if (first int pivot=Partition(r, first, end); QuickSort(r, first, pivot-1); QuickSort(r, pivot+1, end); } } void main(){ int n,r[100],i; cin>>n; for( i=1; i<=n; i++) cin>>r[i]; QuickSort(r,0,n); for( i=1; i<=n; i++) cout< } =========================================== 3、最大子段和 一、蛮力法 #include int BFMaxSum(int a[ ], int n){ int sum=0, i,j; for(i=0; i int s1=0; //s1的复原为零很重要; for(j=i; j s1 += a[j]; if (s1 > sum) sum = s1; //cout< } } return sum; } void main(){ int a[100]; int i,n; cout<<"请输入整数个数:"; cin>>n; cout<<"请输入这"< for(i=0; i cin >> a[i]; int rs=BFMaxSum( a, n); cout<<"该序列的最大字段和为:"< } 二、分治法 #include int MaxSubSum(int a[ ], int left, int right) { int sum=0,i,j; //如果序列长度为1,直接求解 if (left==right) { if (a[left]>0) sum=a[left]; else sum=0; } else { int center=(left+right)/2; //划分 int leftsum=MaxSubSum(a, left, center); int rightsum=MaxSubSum(a, center+1, right); //以下对应情况③,先求解s1 int s1=0, lefts=0; for (i=center; i>=left; i--){ lefts+=a[i]; if (lefts>s1) s1=lefts; } int s2=0, rights=0; //再求解s2 for (j=center+1; j<=right; j++){ rights+=a[j]; if (rights>s2) s2=rights; } sum=s1+s2; //计算情况③的最大子段和 //合并,在sum、leftsum和rightsum中取较大者 if (sum if (sum } return sum; } void main(){ int a[100]; int i,n; cout<<"请输入整数个数:"; cin>>n; cout<<"请输入这"< for(i=0; i cin >> a[i]; int rs=MaxSubSum( a, 0, n-1); cout<<"该序列的最大字段和为:"< } 三、动态规划法 参考动态规划法中的。。 ============================================================== 4、最近点对问题 一、参考 #define inf 9999999 #include #include #include using namespace std; struct point { double x , y; }p[100005]; int a[100005]; //保存筛选的坐标点的索引 bool cmpx( point a , point b){ //结构体数组的排序//这里用的是下标索引 return a.x < b.x; } bool cmpy(int a , int b){ return p[a].y < p[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 min(double a , double b){ return a < b ? a : b; } double closest(int low , int high) { if(low==high) return inf; //1、如果n=1,返回无限大值 int mid = (low + high)>>1; //除2操作可为右移一位; 2、得到中点数 double ans = min( closest(low , mid) , closest(mid + 1 , high) ); //分治法 int i , j , cnt = 0; for(i = low ; i <= high ; ++i) { if(p[i].x >= p[mid].x - ans && p[i].x <= p[mid].x + ans) a[cnt++] = i; //a[]数组记录点的坐标 } sort(a , a + cnt , cmpy); for(i = 0 ; i < cnt ; ++i) { for(j = i+1 ; j < cnt&&j<=i+7 ; ++j) { if(p[a[j]].y - p[a[i]].y >= ans) break; ans = min(ans , dis(p[a[i]] , p[a[j]])); } } return ans; } int main() { int i,n; while(scanf("%d",&n) != EOF) { if(!n) break; for(i = 0 ; i < n ; ++i) scanf("%lf %lf",&p[i].x,&p[i].y); sort(p , p + n , cmpx); printf("%.2lf\n",closest(0 , n - 1)); } return 0; } 二、蛮力法 #include using namespace std; int main() { int n,i,j,x[256],y[256],index1=0,index2=0; int d,min; cin>>n; for(i=0; i cin>>x[i]>>y[i]; //先假定第一个和第二个点间的距离为所有点对的距离的最小值 min=(x[1]-x[2])*(x[1]-x[2])+(y[1]-y[2])*(y[1]-y[2]); //为了避免同一对点计算两次,只考虑i for(i=0; i for(j=i+1; j //d为点对间的距离,运用打擂台的思想 d=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]); //index1,index2记录点对 //min即为最后所有点对间的最小距离值 if(d<=min){ min=d; index1=i+1; index2=j+1; } } cout<<"距离最短的两个点是第"< cout<<"最近的距离的平方是"< return 0; } 三、分治法 与最大子段和的分治法很相像 #include //#include #include #define Max 10000000 using namespace std; int min(int a,int b){ //求两数最小值 return ( a>=b ) ? b : a; } int abs(int a){ //绝对值 return a>0? a: -a; } int dis(int x1, int y1, int x2, int y2){ //距离 cout<<"计算的距离:"< return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2); } int ClosePoints(int n,int s[][2]){ //用二维数组存放点的横纵坐标 //1、点的个数应该大于2 if (n < 2) return Max; if(n==2) return dis(s[0][0],s[0][1],s[1][0],s[1][1]); int m=0,i,j; int s1[256][2],s2[256][2]; //2、m为s中x坐标的中位数 sort(s,s+n); m=(s[0]+s[n-1])/2; // for(i=0; i // m += s[i][0]; // cout<<"s: "< //} //m /= n; //3、构造s1和s2, 使得s1中的x坐标小于m,s2中点的坐标大于m int k=0; int h=0; for(i=0;i { if(s[i][0] { s1[h][0]=s[i][0]; s1[h++][1]=s[i][1]; } else { s2[k][0]=s[i][0]; s2[k++][1]=s[i][1]; } } //4、分治法 cout< int d1=ClosePoints(h,s1); int d2=ClosePoints(k,s2); //5、使得d为S1中的最近距离d1和S2中的最近距离d2的最小值 int d=min(d1,d2); //6、构造p1和p2,使得p1是s1中点的x坐标与m的距离小于d的点集,p2是s2中点的x坐标与m的距离小于d的点集 int c=0,f=0; int p1[256][2],p2[256][2]; for(i=0; i { if(abs(s1[i][0]-m) for(j=0; j<2; j++) p1[c++][j]=s1[i][j]; } //输出s1数组 cout<<"s1: "< } for(i=0; i { if(abs(s2[i][0]-m) for(j=0; j<2; j++) p2[f++][j]=s2[i][j]; } //输出s2数组 cout<<"s2: "< } //7、将p1和p2中点的按y坐标升序排列 int temp; for(i=0; i for(j=1; j if(p1[j-1][1]>p1[j][1]){ temp=p1[j][1]; p1[j][1] = p1[j-1][1]; p1[j+1][1] = temp; temp=p1[j][0]; p1[j][0] = p1[j-1][0]; p1[j+1][0] = temp; } } //输出排序后的p1数组 for(i=0; i cout<<"p1: "< for(i=0; i for(j=1; j if(p2[j-1][1]>p2[j][1]){ temp=p2[j][1]; p2[j][1] = p2[j-1][1]; p2[j+1][1] = temp; temp=p2[j][0]; p2[j][0] = p2[j-1][0]; p2[j+1][0] = temp; } } //输出排序后的p2数组 for(i=0; i cout<<"p2: "< cout< //8、对p1中的每一个点p,在p2中查找与点p的y坐标小于d的点,并求出其中的最小距离dmin int dmin=0; if(c!=0 && f!=0) dmin=dis(p1[0][0],p1[0][1],p2[0][0],p2[0][1]); else return min(d,Max); for(i=0; i for(j=0; j if( abs(p1[i][1]-p2[j][1]) dmin = dis(p1[i][0], p1[i][1], p2[j][0], p2[j][1]); d=dmin; } } } //9、返回最小值d return min(d,dmin); } int main() { int s[256][2]; int n,i,j; cin>>n; //依次输入n个点的x,y坐标 for(i=0; i for(j=0; j<2; j++) cin>>s[i][j]; int rd= ClosePoints(n,s); cout<<"最近点对间的距离为:"; cout< return 0; } ============================== ****************************************** 贪心法 1、多级调度问题 #include #include #include #include using namespace std; struct Data { int data; int index; }; Data t[100]; Data d[100]; bool cmp(Data a,Data b){ //实现结构体数组按照data项进行从大到小排序 return a.data > b.data ; } int sortmin(Data d[], int n){ //寻找d[]数组中的最小值 int i,indexs=0; for(i=0; i if(d[i].data < d[indexs].data ) indexs=i; } return indexs; } void duoji(Data t[], int n, int m){ int i, S[100][100], p[100] , h[100]; Data d[100]; memset(h,0,sizeof(h)); sort(t,t+n,cmp); //结构体的排序 cout< for(i=0; i p[i]=t[i].index; //1、将t[]排序后,按照从大到小将作业号存储在p[]数组中 cout<
} cout< for(i=0; i d[i].data=0; d[i].index=i; } if(n for(i=0; i S[i][0]=p[i] ; d[i].data = t[i].data ; cout<<"第"<
<<"所需时间分别为: "< } } else{ //B、当作业数n > 机器数m的情况 for(i=0; i S[i][0]=p[i] ; d[i].data = t[i].data ; cout<<"第"<
<<"所需时间为: "< } for(i=m; i int j=sortmin(d,m); cout<<"下一次为机器 "< S[j][++h[j]]=p[i]; cout<<" ,并且此次机器"< d[j].data += t[i].data; cout< } } } int main() { int i,n,m; cout<<"输入作业个数n和机器个数m: "; cin>>n>>m; cout<<"输入完成各作业所需要的处理时间:"< for(i=0; i cin>>t[i].data; t[i].index=i; } cout< cout< duoji( t, n, m); return 0; } ==================================================== 2、活动安排问题 4 1 4 3 5 0 6 5 7 #include #include using namespace std; struct Data{ int s,f; }time[100]; bool cmp(Data a, Data b){ return a.f<= b.f; } void ActiveManage(Data time[], bool a[ ], int n) { //各活动的起始时间和结束时间存储于数组s和f中且 //按结束时间的非减序排列 sort(time+1, time+n+1, cmp); int i,j,count, rode[100],k=1; a[1]=1; j=1; count=1; rode[1]=1; for (i=2; i<=n; i++) //从 活动i 开始寻找与 活动j 相容的活动,j从1开始 ,如此循环贪心寻找 { if ( time[i].s >= time[j].f ) { a[i]=1; j=i; rode[++k]=i; count++; } else a[i]=0; } cout <<"共有"< for(i=1; i<=k; i++) cout<<"活动:"< } void main(){ int n,i; bool a[100]; cin>>n; for(i=1; i<=n; i++) cin>>time[i].s>>time[i].f; ActiveManage(time,a,n); } =================================== 3、背包问题 3 20 30 10 60 120 50 50 #include #include using namespace std; struct Data { float data; int index; }; Data origData[100]; bool cmp(Data a, Data b){ return a.data>b.data; } float Bag(float w[],float v[],float C,int n){ int i,x[100]; float vsum=0; float r[100], w1[100], v1[100]; for(i=1; i<=n; i++) r[i]=v[i]/w[i]; for ( i=1; i<=n; i++){ //初始化OrigData[]数组,记录v[i]/w[i] origData[i].data = r[i]; origData[i].index = i; } sort(origData+1,origData+n+1,cmp); //排列OrigData[]数组 又是下标的问题 for(i=1; i<=n; i++){ //根据v[i]/w[i]的降序排列重排列w[]和v[] w1[i]=w[origData[i].index]; v1[i]=v[origData[i].index]; } memset(x,0 , sizeof(x)); i=1; while(w1[i]<=C){ origData[i].data=1; C -= w1[i]; vsum +=v1[i]; i++; } origData[i].data = C/w1[i]; vsum += v1[i] * origData[i].data; //此处纠结了好久。。。 cout<<"所选物品及百分比为:(1代表全选,0代表不选)"< for(int j=1; j<=i; j++) cout< cout< return vsum; } void main(){ int n,i,j; float vsum=0; float w[100],v[100],C; cout<<"输入物品的数量:"; cin>>n; cout<<"输入这"< for(i=1; i<=n; i++) cin>>w[i]; cout<<"输入这"< for(j=1; j<=n; j++) cin>>v[j]; cout<<"输入背包容量C:"< cin>>C; float rs=Bag(w,v,C,n); cout<<"背包中的物品最大价值容量为:"< } ============================================= ********************************************** 动态规划法 1、最大子段和 #include #include using namespace std; //数列b[]代表前j个数的最大子段和(是加上a[j]的值) int DPMaxSum(int a[ ], int n){ int j,b[100]; b[1]=a[1]; //下标为1到n for(j=2; j<=n; j++){ if(b[j-1]>0) b[j]=b[j-1]+a[j]; else b[j] = a[j]; } sort(b+1,b+n+1); //注意数组与此处排序函数的一致性 -------------------------(还有一个递推式) return b[n]; } void main(){ int a[100]; int i,n; cout<<"请输入整数个数:"; cin>>n; cout<<"请输入这"< for(i=1; i<=n; i++) cin >> a[i]; int rs=DPMaxSum(a, n); cout<<"该序列的最大字段和为:"< } ================================================= 2、近似串匹配 5 14 1 happy Have a hsppy day! //-----唯一的问题是不识别空格符 #include int min(int a,int b,int c){ int min=a; if(a>=b) min =b; if(min >=c) min=c; return min; } //P[m]数组存放输入的样本,T[n]数组存放匹配的文本, K-近似匹配 //D[i][j]数组存放样本P前缀p1到pi与文本T前缀T1到Tj的最小差别数 int ASM(char P[ ], char T[ ], int m, int n, int K){ int j,i, D[100][100]; for (j=1; j<=n; j++) //初始化第0行 D[0][j]=0; for (i=0; i<=m; i++) //初始化第0列 D[i][0]=i; for (j=1; j<=n; j++) //根据递推式依次计算每一列 { for (i=1; i<=m; i++) { if ( P[i]==T[j] ) D[i][j]= min( D[i-1][j-1], D[i-1][j]+1, D[i][j-1]+1); else D[i][j]=min( D[i-1][j-1]+1, D[i-1][j]+1, D[i][j-1]+1); } if ( D[m][j]<=K) return j; //输出匹配末位置 } } void main(){ char P[100], T[100]; int i,m,n,K; cout<<"请输入样本字符数m,文本字符数n 和 K值:"< cin>>m>>n>>K; cout<<"请输入样本字符串和文本字符串:"< for(i=1; i<=m; i++) cin>>P[i]; for(i=1; i<=n; i++) cin>>T[i]; cout<<"最大匹配数是:"< } ==================================================== 3、最优二叉树 请输入字符个数n: 4 请输入字符的查找概率 0.1 0.2 0.4 0.3 最优二叉查找树的平均比较次数是:1.7 Press any key to continue #include #define MAX 999 //数组p[]存放n个字符的查找概率 //数组C[i][j]是二叉查找树T(i,j)的平均比较次数 //数组R[i][j]是二叉查找树T(i,j)的根节点序号 double OptimalBST( int n, double p[100 ], double C[100 ][100 ], int R[ 100][100 ] ){ int i,j,d; for (i=1; i<=n; i++) { //初始化 C[i][i-1]=0; C[i][i]=p[i]; R[i][i]=i; } C[n+1][n]=0; for (d=1; d for (i=1; i<=n-d; i++){ j=i+d; double min, sum; int mink; min=MAX; mink=i; sum=0; for (int k=i; k<=j; k++){ sum=sum+p[k]; if (C[i][k-1]+C[k+1][j] min=C[i][k-1]+C[k+1][j]; mink=k; } } C[i][j]=min+sum; R[i][j]=mink; } return C[1][n]; } void main(){ int i, R[100][100] ,n ; double P[100],C[100][100]; cout<<"请输入字符个数n:"< cin>>n; cout<<"请输入字符的查找概率"< for(i=1; i<=n; i++) cin>>P[i]; cout<<"最优二叉查找树的平均比较次数是:"< } ========================================= 4、最长公共子序列 输入两个序列的个数m,n: 6 9 a b c b d b a c b b a b d b b 最长公共子序列是:a c b d b 最长公共子序列的长度是:5 Press any key to continue #include //L[i][j]为序列x[i]与y[j]序列的最长公共子序列的长度 //S[][]记录三种状态,回溯最长公共子序列 int CommonOrder(int m, int n, char x[ ], char y[ ], char z[]){ int L[100][100],i,j,k,S[100][100]; for (j=0; j<=n; j++) //初始化第0行 L[0][j]=0; for (i=0; j<=m; i++) //初始化第0列 L[i][0]=0; for (i=1; i<=m; i++) for (j=1; j<=n; j++) if (x[i]==y[j]) { L[i][j]= L[i-1][j-1] +1; S[i][j] = 1; } else if (L[i][j-1] >= L[i-1][j]) { L[i][j] = L[i][j-1]; S[i][j]=2; } else { L[i][j]=L[i-1][j]; S[i][j]=3; } cout<<"最长公共子序列是:"; i=m; j=n; k=L[m][n]; while (i>0 && j>0) { if (S[i][j]==1) { z[k]=x[i]; k--; i--; j--; } else if (S[i][j]==2) j--; else i--; } for(i=1; i<=L[m][n]; i++) cout< cout< return L[m][n]; } void main(){ char x[100],y[100],z[100]; int n,m; cout<<"输入两个序列的个数m,n:"< cin>>m>>n; for(int i=1; i<=m; i++) cin>>x[i]; for(i=1; i<=n; i++) cin>>y[i]; cout<<"最长公共子序列的长度是:"< } ====================================== 5、0-1背包问题 输入物品数量: 3 输入这3个物品的重量: 20 30 10 输入这3个物品的价值: 60 120 50 输入背包容量C: 50 输出装入背包的物品:1表示装入,0表示未装入 1 1 0 背包中的物品最大价值容量为:180 Press any key to continue #include int max(int a,int b){ return a>b ? a : b; } //V[i][j]表示在前i个物品中能够装入容量为j的背包中的物品的价值最大值 //V[i][0] , V[0][j]都为0; int KnapSack(int n, int w[ ], int v[ ],int C) { //n个物品,C为最大容量 int i,j; int V[100][100],x[100]; for (i=0; i<=n; i++) //初始化第0列 V[i][0]=0; for (j=0; j<=C; j++) //初始化第0行 V[0][j]=0; for (i=1; i<=n; i++) //计算第i行,进行第i次迭代 for (j=1; j<=C; j++) if (j V[i][j]=V[i-1][j]; else //满足装入条件(装入后未超重),但也可不装 V[i][j]=max(V[i-1][j], V[i-1][j-w[i]]+v[i]); //装或者不装后的价值的最大值 //求装入背包的物品:1表示装入,0表示未装入 j=C; for (i=n; i>0; i--){ if (V[i][j]>V[i-1][j]) { x[i]=1; j=j-w[i]; } else x[i]=0; } //输出装入背包的物品:1表示装入,0表示未装入 cout<<"输出装入背包的物品:1表示装入,0表示未装入"< for(i=1; i<=n; i++) cout< cout< return V[n][C]; //返回背包取得的最大价值 } int main(){ int n,i,j,w[100],v[100],C; cout<<"输入物品数量:"< cin>>n; cout<<"输入这"< for(i=1; i<=n; i++) cin>>w[i]; cout<<"输入这"< for(j=1; j<=n; j++) cin>>v[j]; cout<<"输入背包容量C:"< cin>>C; cout<<"背包中的物品最大价值容量为:"< return 0; } ======================================== 6、多段图最短路径 输入城市个数:10 输入城市之间的距离: 100 4 2 3 100 100 100 100 100 100 100 100 100 100 9 8 100 100 100 100 100 100 100 100 6 7 8 100 100 100 100 100 100 100 100 4 7 100 100 100 100 100 100 100 100 100 100 5 6 100 100 100 100 100 100 100 100 8 6 100 100 100 100 100 100 100 100 6 5 100 100 100 100 100 100 100 100 100 100 7 100 100 100 100 100 100 100 100 100 3 100 100 100 100 100 100 100 100 100 100 最短路径长为:16 最短路径为:0->3->5->8->9 Press any key to continue #include //cost[i]表示从顶点i到顶点n-1 的最短路径 //path[i]表示从顶点i到顶点n-1的最短路径上的下一顶点 void duoduantu(int cost[100],int c[100][100],int path[100],int k,int n){ int i,j; for(i=0; i cost[i]=c[i][n-1]; //初始,c[][]记录各顶点间的距离 for(i=n-2; i>=k; i--) for(j=i+1; j if( (cost[j] + c[i][j]) < cost[i]){ cost[i]= cost[j] + c[i][j] ; path[i]=j; } } int main(){ int i,j,n; int c[100][100], cost[100], path[100]; cout<<"输入城市个数:"; cin>>n; cout<<"输入城市之间的距离:"; for(i=0; i for(j=0; j cin>>c[i][j]; duoduantu(cost,c,path,0,n); cout<<"最短路径长为:"< for(i=0; i<=n-2; i++) duoduantu(cost,c,path,i,n); cout<<"最短路径为:"<<0<<"->"; i=0; do{ cout< if(i!=n-2) cout<<"->"; i=path[i]; }while(i cout< return 0; } =========================================