分治法求最大连续和
注意范围是[x,y)
#include<bits/stdc++.h> using namespace std; int maxsum(int *A,int x,int y){ if(y-x==1) return A[x]; int m=x+(y-x)/2; int maxs = max(maxsum(A,x,m),maxsum(A,m,y)); int v,L,R; v=0; L=A[m-1]; for(int i=m-1;i>=x;i--) L=max(L,v+=A[i]); v=0; R=A[m]; for(int i=m;i<y;i++) R=max(R,v+=A[i]); return max(maxs, L+R); } int main() { int A[]={1,-1,2,3,-2}; printf("%d\n",maxsum(A,0,4)); return 0; }
归并排序,注意范围还是[x,y)
归并就是先分治排序最小范围的序列
然后归并,归并紫书上有个图,思路很清晰
#include<bits/stdc++.h> using namespace std; void merge_sort(int *A,int x,int y,int *T){ if(y-x>1){ int m=x+(y-x)/2; int p=x,q=m,i=x; merge_sort(A,x,m,T); merge_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(i=x;i<y;i++) A[i]=T[i]; } } int main() { int A[]={0,2,1}; int T[100]; merge_sort(A,0,3,T); for(int i=0;i<3;i++) printf("%d ",A[i]); return 0; }
逆序对问题
同样的分治归并法,利用归并排序从m往前扫的特点,去数逆序对数
因为序列会排上序,所以直接加(m-p)即可
#include<bits/stdc++.h> using namespace std; int cnt=0; void merge_sort(int *A,int x,int y,int *T){ if(y-x>1){ int m=x+(y-x)/2; int p=x,q=m,i=x; merge_sort(A,x,m,T); merge_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++]; cnt+=m-p; } } for(int i=x;i<y;i++) A[i]=T[i]; } } int main() { int A[]={0,2,1,-1,-3}; int T[100]; merge_sort(A,0,5,T); printf("%d\n",cnt); return 0; }
快速排序:
从起始点选定一个点,从后往前扫,再从前往后扫
再选第二个点,知道选到最后
#include<bits/stdc++.h> using namespace std; void quickSort(int a[],int left,int right) { int i=left; int j=right; int temp=a[left]; if(left >= right) return ; while(i!=j){ while(i<j&&a[j]>=temp) j--; if(j>i) a[i]=a[j]; while(i<j&&a[i]<temp) i++; if(i<j) a[j]=a[i]; } a[i]=temp; quickSort(a,left,i-1); quickSort(a,i+1,right); } int main() { int A[]={0,2,1,-1,-3}; int T[100]; quickSort(A,0,4); for(int i=0;i<5;i++) printf("%d ",A[i]); return 0; }
快速选择问题
根据快速排序划分后的标记点,判断序列要找的k是否大于或者小于它,然后进行方向性的枚举
#include<bits/stdc++.h> using namespace std; int k=2; int quickSort(int a[],int left,int right) { int i=left; int j=right; int temp=a[left]; printf("temp:%d ** \n",a[left]); if(left >= right) return 0; while(i!=j){ while(i<j&&a[j]>=temp) j--; if(j>i) a[i]=a[j]; while(i<j&&a[i]<temp) i++; if(i<j) a[j]=a[i]; } for(int i=0;i<5;i++) printf("%d ",a[i]); printf("\n"); a[i]=temp; printf("i = %d\n",i); if(k<i) quickSort(a,left,i-1); else if(k>i) quickSort(a,i+1,right); else return a[k]; } int main() { int A[]={0,2,1,-1,-3}; // -3 -1 0 1 2 int T[100]; k=0; printf("%d\n",quickSort(A,0,4)); return 0; }
二分查找折半查找
迭代实现
#include<bits/stdc++.h> using namespace std; int bsearch(int *A,int x,int y,int v){ int m; while(x<y){ m=x+(y-x)/2; if(A[m]==v) return m; else if(A[m]>v) y=m; else x=m+1; } return -1; } int main() { int A[]={0,2,1,-1,-3}; // -3 -1 0 1 2 int T[100]; sort(A,A+5); printf("%d\n",bsearch(A,0,5,-3)); return 0; }
二分查找求上下界
STL中也有这两个函数可以直接用
#include<bits/stdc++.h> using namespace std; int lower_bound(int *A,int x,int y,int v){ int m; while(x<y){ m=x+(y-x)/2; if(A[m]>=v) y=m; else x=m+1; } return x; } int upper_bound(int *A,int x,int y,int v){ int m; while(x<y){ m=x+(y-x)/2; if(A[m]<=v) x=m+1; else y=m; } return y; } int main() { int A[]={0,2,1,-1,-3,2}; // -3 -1 0 1 2 int T[100]; sort(A,A+5); printf("%d\n",lower_bound(A,0,5,2)); printf("%d\n",upper_bound(A,0,5,2)); return 0; }
棋牌覆盖问题
递归分治解决,边界为k==1
#include<bits/stdc++.h> using namespace std; int cnt=0; int merge_qipan(int k) { if(k==1) return ++cnt; merge_qipan(k-1); merge_qipan(k-1); merge_qipan(k-1); merge_qipan(k-1); return ++cnt; } int main() { int k; scanf("%d",&k); printf("%d\n",merge_qipan(k)); return 0; }
循环日程表问题:出不来
巨人与鬼:暂时忽略
贪心法:
最优装载问题:纯贪心
部分背包问题:按比值贪心
区间相关问题:选择不相交区间,按b排序
第一种:a1>a2 选择a1 然后:a1<a2<a3从前面不相交的往后选
区间选点问题:按b排序,第一个区间取最后一个点
区间覆盖问题:
预处理:
先切掉[s,t]外的部分
按a排序,起点非s,无解
否则选择起点为s的最长区间
然后新的起点为上一个区间的终点
Huffman编码:
先排序把字符排成表p,创建新节点队列,每次两两合并
构造法:
例题8-1:UVA120煎饼
书上给的分析很明白,其实就是让去找一个可行解,但不知道是不是最优
这种就叫构造算法吧,每次拍一次最大的,最大的排号就不用管了
问题是用数组实现起来真的很麻烦,看到一个666的题解,双端队列,还有reverse翻转,注意翻转是有顺序的。
#include<bits/stdc++.h> using namespace std; int main() { for(string strLine;getline(cin,strLine);cout<<'0'<<endl){ cout<<strLine<<endl; istringstream iss(strLine); deque<int> Stack; for(int nDiam;iss>>nDiam;Stack.push_front(nDiam)) ; for(deque<int>::iterator i=Stack.begin();i!=Stack.end();i++){ deque<int>::iterator iMax = max_element(i,Stack.end()); if(iMax != i) { if(iMax != Stack.end()-1){ reverse(iMax,Stack.end()); cout<<distance(Stack.begin(),iMax) + 1<<' '; } reverse(i,Stack.end()); cout<<distance(Stack.begin(),i)+1<<' '; } } } return 0; }
例题8-2联合国大厦UVa1605
也是按紫书给的思路,共两层,第一层跟i一样放国家,下一层按列,这样每个第一层的国家会和第二层而且是每一个国家都挨着
注意国家只能用大小写字母
#include<bits/stdc++.h> using namespace std; int a[60][60]; int b[60][60]; int main() { int n; while(~scanf("%d",&n)){ printf("%d %d %d\n",2,n,n); for(int i=0;i<n;i++) for(int j=0;j<n;j++){ a[i][j]=i; b[i][j]=j; } for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(a[i][j]>25) printf("%c",a[i][j]-26+'a'); else printf("%c",a[i][j]+'A'); } printf("\n"); } printf("\n"); for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(b[i][j]>25) printf("%c",b[i][j]-26+'a'); else printf("%c",b[i][j]+'A'); } printf("\n"); } printf("\n"); } return 0; }
中途相遇法:从两个方向解决问题,最后汇总
8-3 UVa1152 和为0的四个值
这个题的Hash写的很精妙,好好体会
#include<bits/stdc++.h> using namespace std; struct HashMAP{ static const int mask = 0x7fffff; int p[8388608],q[8388608]; void Clear() { for(int i=0;i<=mask; ++ i) q[i]=0; } int & operator [] (int k){ int i; for(i=k&mask; q[i]&&p[i]!=k;i=(i+1)&mask) ; p[i]=k; return q[i]; } }Hash; int main() { int t; scanf("%d",&t); while(t--){ Hash.Clear(); int n,sum=0,a[4100],b[4100],c[4100],d[4100]; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]); for(int i=0;i<n;i++) for(int j=0;j<n;j++) Hash[a[i]+b[j]]++; for(int i=0;i<n;i++) for(int j=0;j<n;j++) sum+=Hash[-(c[i]+d[j])]; printf("%d\n",sum); if(t) printf("\n"); } return 0; }
问题分解:
例题8-4 传说中的车 UVa11134
和前面讲的区间相关问题类似,首先把图分为两个一维轴,然后根据给定的区间,按b排序,从前面找i的位置,找到便正确
#include<bits/stdc++.h> using namespace std; struct pp { int x1,x2,y1,y2,x,y,num; }B[5050]; bool cmp1(pp a,pp b) { if(a.x2==b.x2) return a.x1<b.x1; else return a.x2<b.x2; } bool cmp2(pp a,pp b) { if(a.y2==b.y2) return a.y1<b.y1; else return a.y2<b.y2; } bool cmp3(pp a,pp b) { return a.num<b.num; } int main() { int n; while(scanf("%d",&n)&&n){ for(int i=0;i<n;i++) { scanf("%d%d%d%d",&B[i].x1,&B[i].y1,&B[i].x2,&B[i].y2); B[i].x=0; B[i].y=0; B[i].num=i+1; } sort(B,B+n,cmp1); // for(int i=0;i<n;i++) printf("%d %d %d %d\n",B[i].x1,B[i].y1,B[i].x2,B[i].y2); int AA=0,BB=0; for(int i=1;i<=n;i++) for(int j=0;j<n;j++) { if(B[j].x1<=i &&B[j].x2>=i &&!B[j].x) { B[j].x=i; AA++; break; } } sort(B,B+n,cmp2); // for(int i=0;i<n;i++) printf("%d %d %d %d\n",B[i].x1,B[i].y1,B[i].x2,B[i].y2); for(int i=1;i<=n;i++) for(int j=0;j<n;j++) { if(B[j].y1<=i &&B[j].y2>=i &&!B[j].y) { B[j].y=i; BB++; break; } } if(AA==n&&BB==n){ sort(B,B+n,cmp3); for(int i=0;i<n;i++) printf("%d %d\n",B[i].x,B[i].y); } else printf("IMPOSSIBLE\n"); } return 0; }
等价转换
例题8-5 UVa11054酒的交易
等价转换就是每一次都只考虑a2家酒庄需要多少劳力给a1家酒庄送酒,结果便是最优
#include<bits/stdc++.h> using namespace std;int main(){ int n;while(cin>>n&&n){ int ans=0,a,last=0; for(int i=0;i<n;i++) {cin>>a;ans+=abs(last);last+=a; } cout<<ans<<endl;} return 0;}
扫描法:扫面法再枚举时维护一些重要的量,简化计算
例题8-6 两性亲子 UVa1606
极角排序的这个题并不会
#include<bits/stdc++.h> using namespace std; struct Node{ public : int x,y,z; double rad; bool operator < (const Node& rhs) const { return rad < rhs.rad; } }c[1000],d[1000]; bool Turn(Node&a,Node&b) { return a.x*b.y>=a.y*b.x; } int main() { int N; while(~scanf("%d",&N)&&N){ for(int i=0;i<N;i++) scanf("%d%d%d",&c[i].x,&c[i].y,&c[i].z); if(N<=2) { printf("%d\n",N); continue; } int maxn=0; for(int i=0;i<N;i++){ int k=0; for(int j=0;j<N;j++){ if(i!=j){ d[k].x = c[j].x-c[i].x; d[k].y = c[j].y-c[i].y; if(c[j].z){ d[k].x = -d[k].x; d[k].y = -d[k].y; } d[k].rad = atan2(d[k].y,d[k].x); k++; } } sort(d,d+k); int L=0;int ans=1;int R=0; while(L<k){ if(L==R){ R=(R+1)%k; ans++; } while(L!=R&&Turn(d[L],d[R])){ ans++; R=(R+1)%k; } maxn = max(maxn,ans); L++; ans--; } } printf("%d\n",maxn); } return 0; }
窗口滑动法
例题8-7 唯一的雪花 UVa11572
其实就是一个循环便利,只不过根据题意如果遇到与当前序列重复的数,就需要从前面找到这个重复的数然后删掉
每次都记录最大的序列长度,便利一遍,即得结果,下面两个实现,一个set,一个map
#include<bits/stdc++.h> using namespace std; const int maxn = 1e6 + 5; int B[maxn]; int main() { int t,N; scanf("%d",&t); while(t--){ scanf("%d",&N); for(int i=0;i<N;i++) scanf("%d",&B[i]); set<int> s; int L=0,R=0,ans=0; while(R<N){ while(R<N&&!s.count(B[R])) { s.insert(B[R]); R++; } ans=max(ans,R-L); s.erase(B[L++]); } printf("%d\n",ans); } return 0; }
用map算出上一次相同数的位置last数组
#include<bits/stdc++.h> using namespace std; const int maxn = 1e6 + 5; int A[maxn],last[maxn]; map<int,int > cur; int main() { int t,n; scanf("%d",&t); while(t--){ scanf("%d",&n); cur.clear(); for(int i=0;i<n;i++){ scanf("%d",&A[i]); if(!cur.count(A[i])) last[i]=-1; else last[i]=cur[ A[i] ]; cur[ A[i] ]=i; } int L=0,R=0,ans=0; while(R<n){ while(R<n&&last[R]<L) R++; ans=max(ans,R-L); L++; } printf("%d\n",ans); } return 0; }
使用数据结构:
再不用改变主算法的情况下,提高运行效率
求f(i)i到k的最小值,要求f(1)...f(n-k+1)
版权声明:本文为博主原创文章,未经博主允许不得转载。