Summary of basic algorithms

Keywords: C++ Algorithm

Bit operation

Pilot brother

Title Link

Problem solving ideas:

Each switch of the chessboard has two states: on or off, so the total number of States is 2 16 2^{16} 216. The range of int is not exceeded, so we can use an integer to represent the state of the chessboard. It means that we change the 4 * 4 matrix into a one-dimensional form, and each bit is 0 or 1. 0 means on, 1 means off (+). When traversing all States, if the I is 1, it means that the i-th switch is pressed, and the change after pressing the switch can be as shown in the figure below, just XOR a value directly. Then check whether the final state is fully open (all 0).

skill

  1. Because XOR satisfies the associative law, we do not need to XOR the values of each row and column one by one, but directly XOR the sum of them once.
  2. The transformation between two-dimensional subscripts and one-dimensional subscripts. The formula for two-dimensional subscript (i,j) to become one-dimensional from 0 is i * n + j. when one-dimensional becomes two-dimensional, x = K / N, y = k% n;

code:

#include <bits/stdc++.h>
using namespace std;

typedef pair <int, int> PII; // Maintain two elements

vector<PII> res; // Save answer

int state; // Compression state 1 means off; 0 means on 

int change[4][4];

int get(int x, int y) // Find the number of two-dimensional matrix (x, y)
{
    return 4 * x + y;
}

int main() {

    char stuc;

    for(int i = 0; i < 4; i ++) {
        for(int j = 0; j < 4; j ++) {

            cin >> stuc;
            if(stuc == '+') {
                state += 1 << get(i, j);
                // Because the current switch is off,
                // Therefore, set the current number of state to 1 by path compression
            }

        }
    }

    for(int i = 0; i < 4; i ++) {
        for(int j = 0; j < 4; j ++) {

            for(int k = 0; k < 4; k ++)
            {
                // Preprocess the XOR value of nine numbers
                change[i][j] += 1 << get(i, k); 
                change[i][j] += 1 << get(k, j);
                // Process the XOR value of row i and column j
            }

            change[i][j] -= 1 << get(i, j);
            // Subtract the same element
        }
    }

    for(int k = 0; k < (1 << 16) ; k ++) { 
    // Enumerates all States of each switch pressed or not pressed
        int now = state;
        // Save the original matrix
        vector<PII> path;

        for(int i = 0; i < 16; i ++) {
            if( (k >> i) & 1) {
            // If k is not off
                int x = i / 4, y = i % 4;
                // Find the (x, y) coordinates of k
                now ^= change[x][y]; // Because it has been preprocessed
                path.push_back({x, y}); // Add temporary answers
            }
        }

        if(!now && ( res.empty() || res.size() > path.size()))
        {
            // Meet the conditions and select the minimum number
            res = path;
        }
    }

    cout << res.size() <<endl; // Number of output elements

    for(int i = 0; i < res.size(); i ++) {
        // Number of output schemes
        cout<< res[i].first + 1<<" "<< res[i].second + 1 <<endl;
    }
}

recursion

fractal

Title Link

Problem solving ideas:

  • This topic is not very difficult to think. We mainly need to see the output format of the topic. In fact, when n > 1, each output can be regarded as a nine house grid.
  • Then we can notice the space between every two X, and we can find a certain rule.

code:

#include <bits/stdc++.h>
using namespace std;
int a[1010][1010],n,m,i,j;
int power(int a,int b)
{
    int ans=1;
    while(b)
    {
        if (b&1)
            ans*=a;
        a*=a;
        b>>=1;
    }
    return ans;
}
void dg(int n,int x,int y)
{
    if (n==1)
    {
        a[x][y]=1;
        return ;
    }
   int len=power(3,n-2);//Enumerating the length of len, that is, enumerating the number of spaces between two X S
    dg(n-1,x,y);//Upper left
    dg(n-1,x+2*len,y);//Upper right
    dg(n-1,x+len,y+len);//middle
    dg(n-1,x,y+2*len);//Lower left
    dg(n-1,x+2*len,y+2*len);//lower right
}
int main()
{
    while(cin>>n && n!=-1)
    {
        memset(a,0,sizeof(a));
        dg(n,1,1);
        for(int i=1;i<=power(3,n-1);i++)
        {
            for(int j=1;j<=power(3,n-1);j++)
                if (a[i][j])
                    cout<<'X';
                else
                    cout<<' ';
            cout<<endl;
        }
        cout<<"-"<<endl;
    }
    return 0;
}

Divide and conquer

Plane nearest point pair

Title Link

Problem solving ideas:
Detailed problem solving

code:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#Include < cmath > / / the sqrt function is required to calculate the distance

using namespace std;

const int N = 200005;
const double INF = 1e15;
const double eps = 1e-6;

int n;
double mind;
struct point // Structure save all points
{
    double x, y;                           // Save the coordinates of each point
    bool type;                             // Save the type of each point
    bool operator < (const point &t) const // Used to sort all points by x coordinate from small to large
    {
        return x < t.x;
    }
} points[N], tmp[N];  // Points stores each input point, and tmp stores the points to be processed for each point during divide and conquer

double get_dist(point a, point b)            // Returns the diameter distance between point a and point b
{
    if (a.type == b.type) return mind ;      // If the types of these two points are different, you can return the current optimal answer.
    double dx = a.x - b.x, dy = a.y - b.y;   // Calculate the difference between the abscissa and ordinate of the two points
    return sqrt(dx * dx + dy * dy);          // Returns the plane distance between these two points
}

double dfs(int l, int r)
{
    if (l == r) return INF ;                 // If there is only one point in the remaining area, in order to avoid updating the answer, positive infinity is returned
    int mid = l + r >> 1;                    // Find the middle point in the remaining area.
    double mid_x = points[mid].x;            // Take out the x coordinate of the point, and the points whose distance from the coordinate exceeds ans are not considered.
    double ans = min(dfs(l, mid), dfs(mid + 1, r)); // Divide and conquer to calculate the above non updated ans

    // First, merge the [l, mid] and [mid + 1, r] segments in points in order according to the y-axis coordinates
    // Note that we must merge here. Later, for each point, we can quickly find the corresponding (at most) 6 points to ensure that the total time complexity is O(n log n)
    int i = l, j = mid + 1, cnt = 0;
    while (i <= mid && j <= r)
        if (points[i].y < points[j].y) tmp[cnt ++ ] = points[i ++ ];
        else    tmp[cnt ++ ] = points[j ++ ];
    while (i <= mid) tmp[cnt ++ ] = points[i ++ ];
    while (j <= r) tmp[cnt ++ ] = points[j ++ ];
    for (i = l; i <= r; i ++ ) points[i] = tmp[i - l];

    // Find all points in [mid_x - ans, mid_x + ans] and store them in tmp
    cnt = 0;
    for (i = l; i <= r; i ++ )
        if (points[i].x >= mid_x - ans && points[i].x <= mid_x + ans) // If the point is mid_ If the distance of X is less than ans, this point needs to be considered
            tmp[cnt ++ ] = points[i];
    // In the second level loop below, the judgment of TMP [i]. Y - TMP [J]. Y < = ans can ensure that we only consider six points at most for each point
    // In this way, in each layer of recursion, we can ensure that the time complexity is linear, otherwise the time complexity is square
    for (i = 0; i < cnt; i ++ ) // Process points in all tmp s
        for (j = i - 1; ~j && tmp[i].y - tmp[j].y + eps <= ans; j -- )
            ans = min(ans, get_dist(tmp[i], tmp[j])); // Update ans
    mind = min(mind, ans);
    return ans;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        scanf("%d", &n);
        for (int i = 0; i < n; i ++ )
        {
            scanf("%lf %lf", &points[i].x, &points[i].y); // Enter the coordinates of all nuclear power plants
            points[i].type = false;                       // The type of nuclear power plant is false
        }
        for (int i = n; i < n << 1; i ++ )
        {
            scanf("%lf %lf", &points[i].x, &points[i].y); // Read in the coordinates of all agents
            points[i].type = true;                        // The agent's type is set to true
        }
        mind = get_dist(points[0], points[(n << 1) - 1]);
        sort(points, points + (n << 1));                  // Sort all points by x coordinate
        printf("%.3lf\n", dfs(0, (n << 1) - 1));          // The return value of the divide and conquer function is the answer
    }
    return 0;
}

Dichotomy

Line of Defense

Title Link

Given n equal difference sequences, the starting point of each equal difference sequence is s, the end point is e, and the difference is d. At most one position in the whole sequence is odd. Judge whether odd digits exist. If not, output "There's no weakness." if not, output position and size.

Detailed problem solution

Personal understanding:

We use the idea of prefix sum to find positions with odd numbers, because we know that there is an odd number in a sequence and the rest are even numbers, so the sum must be odd, so the position we find satisfies that the sum of the front is even and the sum of the back is odd, which is in line with the meaning of the binary answer.

skill:

  • Because we are a binary position, assuming that the position is x, we need to find the number from the starting node to the midpoint of this interval. Have formula this area between package contain this number column of individual number yes ⌊ ( m i n ( e , x ) − s ) / d ⌋ + 1 . The number of this sequence in this interval is ⌊ (min(e,x) − s)/d ⌋ + 1. The number of this sequence in this interval is ⌊ (min(e,x) − s)/d ⌋ + 1.

code:

#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;

typedef long long  ll ;
const int N = 2E5 + 10;
int n;
struct seg{
    ll s,e,d;
}segs[N];

ll get_sum(ll x){
    ll ans = 0;
    for(int i = 0;i< n;++i)
        if(segs[i].s <= x)
            ans += (min(segs[i].e,x) - segs[i].s)/ segs[i].d + 1;
            
    return ans;
}

int main(){
    int T ;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        ll l = 0 , r = 0;
        for(int i = 0;i<n;++i)
           { 
               scanf("%d%d%d",&segs[i].s,&segs[i].e,&segs[i].d);
               r = max(r,segs[i].e);
           }
        while(l < r){
            int mid = (l + r) >> 1;
            // If it is an odd number, the position is in the previous interval
            if(get_sum(mid) & 1) r = mid;
            else l = mid + 1;
        } 
        auto ans = get_sum(l) - get_sum(l - 1);
        if(ans % 2 )
        printf("%d %lld\n",r,ans);
        else 
            puts("There's no weakness.");
        }
        return 0;
    }

Drive cattle into the circle

Title Link

Problem solving ideas:

First, why can we split the answer (the side length of the rectangle), because if there is an optimal solution, the left side of the optimal solution is not in line with the meaning of the question, and the right side is in line with the meaning of the question but not optimal.
So why discretization? Because if we violently detect whether a side length is legal, we can detect it by two-dimensional prefix and enumerating each coordinate, but the maximum number of coordinates is 10000, so the worst is N-square. The title tells us N units, where N < 500. That is, most coordinates are repeated, so we can discretize them.

Discretization

1. Array

//Discretization
void discreate(){
	sort(a+1,a+1+n);
	for(int i = 1;i<=n;++i)
	{
		if(i == 1 || a[i]  != a[i-1])
			b[m++] = a[i];
	}
}
// query
int query(int x){
	return lower_bound(b+1,b+1+m,x) - b;
}

2.vector

//Discretization
void discreate(){
	sort(numbers.begin(),numbers.end());
    numbers.erase(unique(numbers.begin(),numbers.end()) , numbers.end());
}
// query
int query(int x){
	return lower_bound(numbers.begin(),numbers.end(),x) - numbers.begin();
}


code:

#include<stdio.h>
#include<algorithm>
#include<vector>

using namespace std;
typedef pair<int,int> PII;
const int N = 1110;

int n,C;
PII points[N];
int sum[N][N];
vector<int> numbers;

int get(int x){
    return lower_bound(numbers.begin(),numbers.end(),x) - numbers.begin();
}

// Side length is len
bool check(int len){
    int size = numbers.size();
    //After discretization, it becomes an inclusion problem on a one-dimensional line,
    for(int x1 = 0 ,x2 = 1 ; x2 < size;++x2){
        while(numbers[x2] - numbers[x1+1] + 1 > len) x1++;
        for(int y1 = 0 , y2 = 1 ; y2 < size; ++y2){
            while(numbers[y2] - numbers[y1+1] +1 > len) y1 ++ ;
            if((sum[x2][y2] - sum[x1][y2] - sum[x2][y1] + sum[x1][y1]) >= C)
                return true;
        }
    }
    return false;
}

int main(){
    scanf("%d%d",&C,&n);
    numbers.push_back(0);
    for(int i = 0 , x, y ;i < n ;++i){
        scanf("%d%d",&x,&y);
        points[i] = {x,y};
        numbers.push_back(x);
        numbers.push_back(y);
    }
    sort(numbers.begin(),numbers.end());
    numbers.erase(unique(numbers.begin(),numbers.end()) , numbers.end());
    // Initialize prefix and
    // Equivalent to sum[x][y] will be accumulated if the X and Y coordinates are repeated
        for(int i = 0 ;i<n;++i){
            int x = get(points[i].first) , y = get(points[i].second);
            sum[x][y] ++;
        }
        for(int i = 1;i<numbers.size();++i)
            for(int j = 1;j<numbers.size();++j)
                sum[i][j] +=  sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1];
        // Two point answer
        int l = 1 , r = 10000;
        while( l < r){
            int mid = l + r >> 1;
            if(check(mid)) r = mid;
            else l = mid + 1;
        }
        printf("%d\n",r);
    
    return 0;
}

median

Candy transfer [ring card sharing problem]

Title Link

Problem solving ideas:


From the above formula, we can transform the problem into n points on a given number axis and find a point where the sum of distances to them is as small as possible, and this point is the median of these numbers. The important thing is how to get the c[i] array. From the above figure, we get c [1] = - B + A1, c [2] = - 2b + A1 + A2 = c [1] + a2 - B,..., and we can calculate c through iteration. Also note that c[n] = 0;

code:

#include<stdio.h>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll ;
const int N = 1e6 + 10;
int   a[N], n ;
ll c[N], sum,avg;

int main(){
    sum = 0;
    scanf("%d",&n);
    for(int i = 0; i< n;++i)
        {
            scanf("%d",&a[i]);
            sum += a[i];
        }
     avg = sum / n;
    // c[1] = -b + a1 ,c[2] = -2b + a1 + a2 = c[1] + a2 - b
    for(int i = 1 ;i<= n;++i)
        c[i] = c[i-1] + a[i] - avg;
    c[n] = 0; // The last special judgment is 0
    sort(c+1,c+n+1);
    long long ans = 0;
    for(int i = 1;i<=n;++i)
        ans += abs (c[i] - c[(n>>1) +1]);
    
    printf("%lld\n",ans);
    return 0;
}

rank-and-file soldiers

Title Link

Problem solving ideas:

The problem is to minimize the sum of the moving distances of X and y, and X and y are independent. For the y-axis, we can regard it as a warehouse problem, as long as we arrange and accumulate the distance from each point to the median. For the x-axis, because it is necessary to ensure that the x-coordinates are adjacent and not repeated, there is a property for the optimal number of times that the relative position of the x-coordinates will not change, that is, if you are in front of me, you should still be in front of me after moving,
Let the leftmost soldier go to x1=a, then the second soldier goes to x2=a+1, and the i soldier goes to xi=a+i − 1. Then, the i soldier has to go | Xi − (a+i − 1)|=|xi − (i − 1) − a | Xi − (a+i − 1)|=|xi − (i − 1) | = | a | unit distance
If xi − (i − 1) is regarded as a whole, the problem will be transformed into the problem of "warehouse location". Sort and take the median


Here a is unknown and x is known.

be careful:

If the subscript starts from 0, the subscript corresponding to the median should be n/2. If the subscript starts from 1, it is (n+1) / 2

code;

#include<stdio.h>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;

const int N = 1E4 + 10;

int x[N] , y[N];
int n;

int main(){
    scanf("%d",&n);
    for(int i = 0;i<n;++i)
        scanf("%d%d",&x[i],&y[i]);
    // You have to order the x coordinates first
    sort(x,x+n);
    for(int i = 0;i<n;++i)
        x[i] -= i;
    sort(x,x+n),sort(y,y+n);
    ll ans = 0;
    for(int i = 0;i<n;++i)
    {
        ans += abs( y[i] - y[n /2]);
        ans += abs( x[i] - x[n/2]);
    }
    printf("%lld\n",ans);
    return 0;
}

greedy

Acrobatic cow

Title Link

Problem solving ideas
We consider the i-th cow and the I + 1st cow adjacent to each other

The risk value of other cattle is obviously unchanged, so the maximum risk value of the two cattle before and after the exchange can be analyzed.

In order to be optimal before exchange, the maximum before exchange must be less than the maximum after exchange. obvious w i − s i + 1 > − s i + 1 w_i - s_{i+1} > -s_{i+1} wi − si+1 > − si+1, so we just w i − s i + 1 < w i + 1 − s i w_i - s_{i+1} <w_{i+1} -s_{i} Wi − si+1 < wi + 1 − si can be obtained by sorting w i + s i < w i + 1 − s i + 1 w_i + s_{i} < w_{i+1} -s_{i+1} wi​+si​<wi+1​−si+1​

code:

#include<stdio.h>
#include<algorithm>

using namespace std;

typedef long long ll;

const int N = 5E4+10;

int n;
struct cow{
    ll w,s;
}cows[N];


//Sort from small to large, and put the small at the top
bool cmp(cow a,cow b){
    return a.w + a.s < b.w + b.s;
}

int main(){
    scanf("%d",&n);
    for(int i = 0;i<n;++i)
        scanf("%lld%lld",&cows[i].w,&cows[i].s);
    sort(cows,cows + n,cmp);
    ll sum = 0 , ans =- 98765421;
    for(int i = 0;i<n;++i)
    {
        ll temp =  sum - cows[i].s;
        ans = max(ans,temp);
        sum += cows[i].w;
    }

    printf("%lld\n",ans);
    return 0;
}

task

Title Link

Four ways to solve problems:

We found that this question x is the dominant income. As long as X is large, we don't need to consider y. therefore, we use X as the first keyword and y as the second keyword for descending order (ascending order is also OK. When traversing, we can traverse backwards). By placing X and Y on the coordinate axis, we can know that the machine that can meet the task must be at the top right of the task point. Then we consider that for the machine whose x is greater than the task, we sort y from small to large, and select the least suitable machine for operation.

code:

#include<stdio.h>
#include<set>
#include<algorithm>

using namespace std;

typedef pair<int ,int >PII;
typedef long long ll;
const int N = 1E5 + 10;
int n,m;
PII machs[N],tasks[N];

int main(){
    while(~scanf("%d%d",&n,&m)){
        for(int i = 0;i<n;++i)scanf("%d%d",&machs[i].first,&machs[i].second);
        for(int i = 0;i<m;++i)scanf("%d%d",&tasks[i].first,&tasks[i].second);
        //PII is arranged in ascending order with the first keyword as the first keyword and the second keyword as the second keyword by default
        sort(machs,machs + n);
        sort(tasks,tasks + m);
        multiset<int>ys;
        ll cnt = 0 ,res = 0;
        // So it's going back and forth
        for(int i = m-1,j = n-1;i>=0;i--){
            int x = tasks[i].first , y = tasks[i].second;
            // Add the qualified machines, because set will sort the elements from small to large by default
            while(j>= 0 && machs[j].first >= x) ys.insert( machs[j--].second);
            auto it = ys.lower_bound(y); // Finding a bug that matches is the worst
            if(it != ys.end()){
                cnt ++ ;
                res += 500 * x + 2* y;
                ys.erase(it);
            }
        }
        printf("%lld %lld\n",cnt,res);
    }
    
    return  0;
}

Prefix and

Sum of maximum submatrix (two-dimensional column prefix sum)

Title Link

Problem solving ideas:

Let's recall our approach to finding the maximum continuous interval sum in one dimension. Our approach is dynamic programming, where d p [ i ] of contain righteousness yes i of most large even Continued area between of and , d p [ i ] = m i x ( 0 , d p [ i − 1 ] ) + a [ i ] dp[i] means the sum of the maximum continuous intervals of I, dp[i] = mix(0,dp[i-1]) + a[i] dp[i] means the sum of the maximum continuous interval of I, dp[i]=mix(0,dp[i − 1])+a[i]. Therefore, we can use this idea to turn this problem into a O ( n 3 ) O(n^3) O (n3). See the figure below

Therefore, the idea is to cycle through the upper and lower boundaries of the matrix twice. Note that it can be a straight line, and then take the sum of all columns of this boundary as a number to become the maximum continuous interval sum. However, we can quickly find the sum of all columns by using prefix and optimization,

code:

#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<limits.h>
using namespace std;
const int N = 110;

int g[N][N];
int n;

int main(){
    scanf("%d",&n);
    for(int i = 1 ;i<= n ;++i)
        for(int j = 1;j<=n;++j)
        {
            scanf("%d",&g[i][j]);
            g[i][j] += g[i-1][j]; //Prefix and prefix each column
        }
    int res = INT_MIN;
    for(int i = 1;i<=n;++i)
        for(int j = i;j<=n;++j){
            int last = 0;
            for(int k = 1;k<=n;++k){
                // g[j][k] - g[i-1][k] is the sum of all numbers in row i ~j of column K
                last = max(0,last) + g[j][k] - g[i-1][k];
                res = max(res,last);
            }
        }
        printf("%d\n",res);
    
    return 0;
}

Posted by thallium6 on Sat, 06 Nov 2021 18:01:47 -0700