• 分治算法求解:逆序对,Max Sum,棋盘覆盖,a-Good String——中山大学软件工程学院算法第四次实验课 必做+选做题


    写英文注释不是要“秀英文”,而是因为鄙人正在准备雅思,顺手练习

    逆序对

    题目描述

    完整代码

    1. #include
    2. using namespace std;
    3. int num[500010]; // input numbers
    4. int tmp[500010]; // sequence after merging left and right part
    5. long long res;// Count of inversions
    6. void merge(int left,int mid,int right){
    7. int i=left,j=mid+1;
    8. int idx=0;
    9. while(i<=mid&&j<=right){
    10. if(num[i]>num[j]){
    11. tmp[idx++]=num[j++];
    12. res+=mid-i+1;
    13. }
    14. else
    15. tmp[idx++]=num[i++];
    16. }
    17. while(i<=mid)
    18. tmp[idx++]=num[i++];
    19. while(j<=right)
    20. tmp[idx++]=num[j++];
    21. }
    22. void merge_sort(int left,int right){
    23. if(left>=right){
    24. return;
    25. }
    26. int mid=(left+right)/2;
    27. merge_sort(left,mid);
    28. merge_sort(mid+1,right);
    29. merge(left,mid,right);
    30. for(int i=left;i<=right;i++){
    31. num[i]=tmp[i-left]; //num数组不是tmp数组翻转过来!
    32. //这里是调整索引
    33. }
    34. }
    35. int main(){
    36. ios::sync_with_stdio(false);
    37. int n;
    38. cin>>n;
    39. for(int i=0;i
    40. cin>>num[i];
    41. }
    42. res=0;
    43. merge_sort(0,n-1);
    44. cout<
    45. }

    代码讲解 

    归并排序。在合并的过程发现逆序的时候,后面有多少个就说明有多少个逆序了。

    1. for(int i=left;i<=right;i++){
    2. num[i]=tmp[i-left]; //num数组不是tmp数组翻转过来!
    3. //这里是调整索引
    4. }

    注意这里的索引调整 i-left 是因为 tmp 数组是从 0 开始的,而 num 数组的相应段是从 left 开始的。

    Max Sum

    题目描述

    完整代码

    1. #include
    2. #include
    3. using namespace std;
    4. int num[100010];
    5. struct node{
    6. int l,r,sum;
    7. node(int x,int y,int z)
    8. :l(x),r(y),sum(z){}//constructor initialization
    9. };
    10. node crossingSubArray(int num[],int mid,int low,int high){
    11. node s3(0,0,0);
    12. int sum=0;
    13. int max_left=-10000;// max value for the left part
    14. int max_right=-10000;// max value for the right part
    15. // A very sophisticated algorithm. Since this array crosses both the left and right arrays, the middle must be crossed!
    16. // For the left part, the sequence starts from the middle and goes to the low index. If there is an index that achieves a larger value, update the left index.
    17. for(int i=mid;i>=low;i--){
    18. sum+=num[i];
    19. if(sum>=max_left){
    20. max_left=sum;
    21. s3.l=i;
    22. }
    23. }
    24. sum=0;
    25. // possess the right array in the same way
    26. for(int i=mid+1;i<=high;i++){
    27. sum+=num[i];
    28. if(sum>max_right){
    29. max_right=sum;
    30. s3.r=i;
    31. }
    32. }
    33. s3.sum=max_left+max_right;
    34. return s3;
    35. }
    36. node maxSubArray(int num[],int left,int right){
    37. if(left==right){//means that there is only one number
    38. return node(left,right,num[left]);
    39. }
    40. int mid=(left+right)/2;
    41. node s1= maxSubArray(num,left,mid);
    42. node s2= maxSubArray(num,mid+1,right);
    43. node s3= crossingSubArray(num,mid,left,right);
    44. if(s1.sum>=s2.sum&&s1.sum>=s3.sum)
    45. return s1;
    46. else if(s2.sum>=s1.sum&&s2.sum>=s3.sum)
    47. return s2;
    48. else
    49. return s3;
    50. }
    51. int main(){
    52. int T,n;
    53. ios::sync_with_stdio(false);
    54. cin>>T;
    55. int ncase=0;
    56. while(T--){
    57. cin>>n;
    58. memset(num,0,sizeof(num));
    59. for(int i=0;i
    60. cin>>num[i];
    61. }
    62. node res=maxSubArray(num,0,n-1);
    63. if(ncase){//equivalent to if(ncace!=0)
    64. cout<
    65. }
    66. cout<<"Case "<<++ncase<<":\n";
    67. cout<" "<1<<" "<1<
    68. }
    69. }

    代码讲解

    分治法解决的思路是:

    把数组对半分,使得和最大的左和右索引,要么包括了左半边,要么包括了右半边,要么一个在左边,一个在右边。所以在这三个中取最大值即可。如果只在某半边,因为最后会把那个块拆成单个,所以是可以直接通过比较比出来。如果是穿过了中间,注意要从中间往两边增(拆成先往左增再往右增)来比较怎么样才是最大的并且记下来此时的下标。

    棋盘覆盖问题

    题目描述

    完整代码

    1. #include
    2. #include
    3. using namespace std;
    4. int tile=1;// index of tile
    5. void tileBoard(vectorint> >& board,int x,int y,int specialX,int specialY,int size){
    6. if(size==1) return;
    7. int half=size/2;
    8. int currentTile=tile++;
    9. // top-left corner
    10. // if the special block is in the top-left corner, solve the top-left corner first.
    11. // if not, the top-left block among the four in the center can be determined as the place where the current tile should be placed
    12. if (specialX < x + half && specialY < y + half) {
    13. tileBoard(board, x, y, specialX, specialY, half);
    14. } else {
    15. board[x + half - 1][y + half - 1] = currentTile;
    16. tileBoard(board, x, y, x + half - 1, y + half - 1, half);
    17. }
    18. if (specialX < x + half && specialY >= y + half) {
    19. tileBoard(board, x, y + half, specialX, specialY, half);
    20. } else {
    21. board[x + half - 1][y + half] = currentTile;
    22. tileBoard(board, x, y + half, x + half - 1, y + half, half);
    23. }
    24. // bottom-left corner
    25. if (specialX >= x + half && specialY < y + half) { // in the bottom-left corner
    26. tileBoard(board, x + half, y, specialX, specialY, half); // find the bottom-left corner
    27. } else { // special block is not in the bottom-left corner
    28. board[x + half][y + half - 1] = currentTile; // put current tile in BL corner
    29. tileBoard(board, x + half, y, x + half, y + half - 1, half); // find the bottom-left corner
    30. }
    31. // bottom-right corner
    32. if (specialX >= x + half && specialY >= y + half) {
    33. tileBoard(board, x + half, y + half, specialX, specialY, half);
    34. } else {
    35. board[x + half][y + half] = currentTile;
    36. tileBoard(board, x + half, y + half, x + half, y + half, half);
    37. }
    38. }
    39. int main(){
    40. int k,x,y;
    41. cin>>k>>x>>y;
    42. int size=(1<
    43. vectorint> > board(size,vector<int>(size,0));
    44. // a quick way to initialize the 2D vector to 0
    45. // The number of dimensions in the vector corresponds to the number of levels of nesting
    46. //NOTE:the coordinate offset
    47. board[x-1][y-1]=0;
    48. tileBoard(board,0,0,x-1,y-1,size);
    49. for(const auto & row:board){
    50. for(int i=0;isize();i++){
    51. cout<size()-1?'\n':' '); // A simple but useful judgement
    52. }
    53. }
    54. }
    55. //the board look like this
    56. /*
    57. * y 0 1 2
    58. * x 0 TL TR
    59. * 1 BL BR
    60. * 2
    61. *
    62. */

    代码讲解

    这题的思路还有点绕。就是先把棋盘分成4大块。先看特殊方块在不在左上,如果在的话递归进入左上搜索。如果不在,说明左上最靠近中心(也就是左上大块最右下的小块)标记为当前的骨牌。其余几个同理。

    a-Good String

    题目描述

    完整代码

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. string str;
    6. int solve(int left,int right,char c){
    7. if(left==right){
    8. if(c==str[left])
    9. return 0;
    10. else
    11. return 1;
    12. }
    13. int mid =(left+right)/2;
    14. int res1=0,res2=0;
    15. for(int i=left;i<=mid;i++){
    16. if(str[i]!=c) {
    17. res1++;
    18. }
    19. }
    20. for(int i=mid+1;i<=right;i++){
    21. if(str[i]!=c) {
    22. res2++;
    23. }
    24. }
    25. return min(res1 + solve(mid + 1, right, c + 1), res2 + solve(left, mid, c + 1));
    26. }
    27. int main(){
    28. int T,n;
    29. cin>>T;
    30. while(T--){
    31. cin>>n>>str;
    32. cout<<solve(0,n-1,'a')<
    33. }
    34. return 0;
    35. }

    代码讲解

    先拆分。用个solve函数返回当前字符串变多少个能成为good string。退出条件:如果最后只剩一个了,这个是指定的c,那就不用变,为0;否则,要变一个,为1。

    然后尝试字符串前一半为a,根据题意,此时如果前一半中有不是a的,要变的次数+1。然后加上递归后一半中要变的。

    同理再尝试字符串的后一半为a,根据题意,此时如果后一半中有不是a的,要变的次数+1。然后加上递归前一半中要变的。

    最后,返回前后对比变的次数更少的那一个。

  • 相关阅读:
    LabVIEW创建自由标签 关联注释
    Jenkins-CentOS安装jenkins
    RDD行动算子和血缘关系
    OpenGL - Gamma Correction
    C++之生成详细汇编代码(二百一十六)
    【计算机视觉40例】案例10:隐身术
    deque容器使用及评委打分系统
    Mysql子查询
    LeetCode 面试题 16.17. 连续数列
    【STM32】STM32中断体系
  • 原文地址:https://blog.csdn.net/weixin_64123373/article/details/133210954