算法导论题集(后续更新)

注:该习题为本人做题时整理,仅供参考。
算法导论题集:http://47.99.179.148/problemlist.php

重点题: 分治 1004 动态规划 1009 1018 1020 1024 1026 1034 1042 贪心 1033 1031 1030

一、基本排序问题

0、A+B=C

#include <iostream>
#include <vector>

using namespace std;

int main() 
{
    int n, a, b;
    cin >> n;
    vector<int> vec;
    for (int i = 0; i < n; i++) 
    {
        cin >> a >> b;
        vec.push_back(a + b);
    }
    for (int j = 0; j < n; j++)
    {
        cout << vec[j] << endl;
    }
    return 0;
}

1、统计数字个数

#include <stdio.h>

int main() {
    int n;
    char c[110];
    scanf("%d", &n);
    fgets(c, 110, stdin);
    for(int i = 0; i < n; i++) {
        int count = 0;
        fgets(c, 109, stdin);
        for (int j = 0; c[j] != '\0'; j++) {
            if ('0' <= c[j] && c[j] <= '9') {
                count++;
            }
        }
        printf("%d\n", count);
    }
}

2、找第2小数

#include<iostream>

using namespace std;

int find_2_k(int a[], int m,int k)
{
    int result=0; //返回结果
    int numberOfLess; //假如要找第3小的数,numberOfLess= 2,以此类推
    for (int i = 0; i < m; i++)
    {
        result = a[i];
        numberOfLess = 0;
        for (int j = 0; j < m; j++)
        {

            if (j != i) //无需判断自身
            {
                if (a[j] < a[i])
                {
                    numberOfLess++; //记录比a[i]小的数
                }
            }

        }
        if (numberOfLess == k - 1)//当找到第k个小的数时,跳出整个循环
            break;
    }
    return result; //返回找到的数
}

int main()
{
    int m;
    int k = 2; //找第二小的数
    int a[1000];
	cin >> m;
    for (int i = 0; i < m; i++)
    {
        int n;
        int min_2;
        cin >> n;
        for (int j = 0; j < n; j++)
        {
            cin >> a[j];
        }
        min_2 = find_2_k(a,n,k);
        cout << min_2 << endl;
    }
    return 0;
}

3、冒泡排序

#include<iostream>
using namespace std;
int main()
{
	int m;
	int a[1000];
	cin >> m;
	for (int i = 0; i < m; i++)
	{
		int n;
		cin >> n;
		// 输入数据
		for (int j = 0; j < n; j++)
		{
			cin >> a[j];
		}
		//一次冒泡排序
		for (int k = 0; k < n - 1; k++)
		{
			int temp;
			if (a[k] > a[k + 1])
			{
				temp = a[k];
				a[k] = a[k + 1];
				a[k + 1] = temp;
			}
		}
		//输出冒泡排序后的数组
		for (int w = 0; w < n; w++)
		{
			cout << a[w] << " ";
		}
		cout << endl;
	}
	return 0;
}

4、归并排序

https://www.cnblogs.com/Arthas8086/p/12059342.html

注:下面这个算法是归并排序的最终结果,第三层的排序结果没想到实现思路。

#include <iostream>

using namespace std;

int a[1000] = {0};

void Merge(int* numbers, int start, int mid, int end) {
	int* temp = new int[end - start + 1];	//第一步,申请空间,大小为两个排序序列之和
	int fistSectionIndex = start;			//第二步,设定两个待排序列的起始位置的索引
	int secondSectionIndex = mid + 1;
	int tempIndex = 0;	//所申请空间的索引

	while (fistSectionIndex <= mid && secondSectionIndex <= end) {	//直到两个序列中有一个到达终止位置
		if (numbers[fistSectionIndex] <= numbers[secondSectionIndex])
			temp[tempIndex++] = numbers[fistSectionIndex++];
		else
			temp[tempIndex++] = numbers[secondSectionIndex++];
	}

	while (fistSectionIndex <= mid)
		temp[tempIndex++] = numbers[fistSectionIndex++];

	while (secondSectionIndex <= end)
		temp[tempIndex++] = numbers[secondSectionIndex++];

	for (int j = 0; j < tempIndex; ++j)		//将合并且排序好的元素,复制到原来的数组中,释放临时数组空间
		numbers[start + j] = temp[j];
	delete[] temp;
	
}

void MergeSort(int* numbers, int start, int end) {
	if (numbers == NULL || start >= end)
		return;
	int mid = (start + end) / 2;
	MergeSort(numbers, start, mid);		//递归排序numbers[start,mid](首先从上往下递归分解到最底层元素个数为1的情况)
	MergeSort(numbers, mid + 1, end);	//递归排序numbers[mid + 1,end](首先从上往下递归分解到最底层元素个数为1的情况)
	Merge(numbers, start, mid, end);	//然后递归的从下往上合并排序
}

int solve()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	MergeSort(a, 0, n-1);
	for (int j = 0; j < n; j++)
		cout << a[j] << " ";
	cout << endl;
	return 0;
}

int main() {
	int m;
	cin >> m;
	for (int i = 0; i < m; i++)
	{
		solve();
	}
	return 0;
}

5、快速排序

#include <stdio.h>
int res[1000] = { 0 };
int a[1000] = { 0 };
int n;
int quicksort(int low, int high, int depth) {
    if (low >= high) {
        return 0;
    }
    if (low + 1 == high) {
        if (a[low] > a[high]) {
            int temp = a[low];
            a[low] = a[high];
            a[high] = a[low];
        }
        return 0;
    }
    int p = low;
    for (int i = low; i <= high; i++) {
        if (a[low] > a[i]) {
            p++;
            int temp = a[p];
            a[p] = a[i];
            a[i] = temp;
        }
    }
    int temp = a[low];
    a[low] = a[p];
    a[p] = temp;

    if (depth == 1) {
        res[p] = a[p];
    }
    if (depth == 2) {
        for (int i = low; i <= high; i++) {
            res[i] = a[i];
        }
    }

    quicksort(low, p - 1, depth + 1);
    quicksort(p + 1, high, depth + 1);

    return 0;
}

int solve() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }

    quicksort(0, n - 1, 1);

    for (int i = 0; i < n; i++) {
        printf("%d ", res[i]);
    }
    printf("\n");
    /*
    for(int i=0; i<n; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");*/
    return 0;
}

int main() {
    int m;
    scanf("%d", &m);
    for (int i = 0; i < m; i++) {
        solve();
    }
    return 0;
}

6、堆排序

#include <iostream>
#include <algorithm>
using namespace std;

int a[1000];

void adjust_heap(int* a, int node, int size)
{
    int left = 2 * node + 1;
    int right = 2 * node + 2;
    int min = node;
    if (left < size && a[left] < a[min])
        min = left;
    if (right < size && a[right] < a[min])
        min = right;
    if (min != node)
    {
        swap(a[min], a[node]);
        adjust_heap(a, min, size);
    }
}

void heap_sort(int* a, int len)
{
    for (int i = len / 2 - 1; i >= 0; --i)
        adjust_heap(a, i, len);
}

int solve()
{
    int n;
    int depth = 1;
    cin >> n;
    //输入数据
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    //堆排序
    heap_sort(a, n);
    //输出数据
    for (int j = 0; j < n; j++)
    {
        cout << a[j] << " ";
    }
    cout << endl;
    return 0;
}
int main()
{
    int m;
    cin >> m;
    for (int i = 0; i < m; i++)
    {
        solve();
    }
    return 0;
}

堆排序完整过程:https://blog.csdn.net/pursue_my_life/article/details/80253469

#include <iostream>
using namespace std;
 
void adjust_heap(int* a, int node, int size)
{
        int left = 2*node + 1;
        int right = 2*node + 2;
        int max = node;
        if( left < size && a[left] > a[max])
                max = left;
        if( right < size && a[right] > a[max])
                max = right;
        if(max != node)
        {
                swap( a[max], a[node]);
                adjust_heap(a, max, size);
        }
}
 
void heap_sort(int* a, int len)
{
        for(int i = len/2 -1; i >= 0; --i)
                adjust_heap(a, i, len);
 
        for(int i = len - 1; i >= 0; i--)
        {
                swap(a[0], a[i]);           // 将当前最大的放置到数组末尾
                adjust_heap(a, 0 , i);              // 将未完成排序的部分继续进行堆排序
        }
}
 
int main()
{
 
        int a[10] = {3, 2, 7, 4, 2, -999, -21, 99, 0, 9  };
        int len= sizeof(a) / sizeof(int);
 
        for(int i = 0; i < len; ++i)
                cout << a[i] << ' ';
        cout << endl;
 
        heap_sort(a, len);
 
        for(int i = 0; i < len; ++i)
                cout << a[i] << ' ';
        cout << endl;
 
        return 0;
}

7、最大乘积

思路:动态规划 https://blog.csdn.net/qq_42818329/article/details/90269910

#include<iostream>
#include<string.h>

using namespace std;

int max(int a, int b)
{
	return a>b?a:b;
}

int solve()
{
	int k, N;
	cin>>N>>k;//M是乘号个数, N是字符串长度
	char str[N+1];
	cin>>str;
	int dp[N+1][k+1];
	int data[N+1][N+1];
	memset(dp, 0, sizeof(dp));
	for(int i=0;i<N;i++)
	{
		int jojo=0;
		for(int j=i;j<N;j++)
		{
			jojo=jojo*10+str[j]-'0';
			data[i][j]=jojo;
		}
	}
	for(int i=0;i<N;i++)
	{
		dp[i][0]=data[0][i];
	}
	for(int i=0;i<N;i++)
	{
		for(int j=1;j<=k;j++)
		{
			for(int c=0;c<i;c++)
			{
				dp[i][j]=max(dp[c][j-1]*data[c+1][i], dp[i][j]);
			}
		}
	}
	cout<<dp[N-1][k]<<endl;
}
int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		solve(); 
	}
	return 0;
}

8、拦截导弹2

#include<cstdio>
#include<iostream>
using namespace std;
int a[100], f[100], g[100] = { 0,1 };
int maxn=0,minn=0;

int main()
{
    int m;
    cin >> m;
    for (int i = 0; i < m; i++)
    {
        int n;
        cin>>n;
        for (int i = 1; i <= n; i++)
        {
            cin>>a[i]; //输入每枚导弹的高度
            f[i] = g[i] = 1;
            for (int j = 1; j < i; j++)
            {
                if (a[i] <= a[j]) //求最长下降子序列
                {
                    f[i] = max(f[i], f[j] + 1);
                }
                if (a[i] > a[j]) //求最长上升子序列
                {
                    g[i] = max(g[i], g[j] + 1);
                }
            }
            if (f[i] > minn) //求最长下降子序列
            {
                minn = f[i];
            }
            if (g[i] > maxn) //求最长上升子序列
            {
                maxn = g[i];
            }
        }
        cout << minn << " " << maxn<<endl;
        minn = maxn = 0;
    }
    return 0;
}

9、拦截导弹1

#include<cstdio>
#include<iostream>
using namespace std;
int a[100], f[100];
int maxn=0,minn=0;

int main()
{
    int m;
    cin >> m;
    for (int i = 0; i < m; i++)
    {
        int n;
        cin>>n;
        for (int i = 1; i <= n; i++)
        {
            cin>>a[i]; //输入每枚导弹的高度
            f[i] = 1;
            for (int j = 1; j < i; j++)
            {
                if (a[i] <= a[j]) //求最长下降子序列
                {
                    f[i] = max(f[i], f[j] + 1);
                }
            }
            if (f[i] > minn) //求出最长上升子序列的长度
            {
                minn = f[i];
            }
        }
        cout << minn <<endl;
        minn = 0;
    }
    return 0;
}

二、分治递归问题

10、二分搜索

#include <iostream>
using namespace std;

int a[20000];
int father = 0;

int binarySearch(int arr[], int low, int high, int target)//递归实现
{
    int mid = (low + high) / 2;
    if (low > high)
    {
        cout << "not found, father is " << father << endl;
        return -1;
    }

    if (arr[mid] == target)
    {
        cout << "success, father is " << father << endl;
        return mid;
    }

    if (arr[mid] > target)
    {
        father = arr[mid];
        return binarySearch(arr, low, mid - 1, target);
    }
    if (arr[mid] < target)
    {
        father = arr[mid];
        return binarySearch(arr, mid + 1, high, target);
    }

}

int solve()
{
    int n;               //表示n个递增有序数组
    cin >> n;
    int x;              //表示将要搜索的元素
    cin >> x;
    //输入数据
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    //查找元素
    binarySearch(a, 0, n-1, x);
   
}
int main()
{
    int m;
    cin >> m;
    for (int i = 0; i < m; i++)
    {
        solve();
    }
    return 0;
}

11、最近点对

思路:分治法 https://blog.csdn.net/weixin_41008021/article/details/90017526

https://www.cnblogs.com/zuoyou151/p/9059903.html

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct position_t {
    int x;
    int y;
} pos[100001], tpos[100001], swappos[100001];
int n;

double mydistance(int i, int j) {
    return 1.0*(pos[i].x-pos[j].x)*(pos[i].x-pos[j].x) + 1.0*(pos[i].y-pos[j].y)*(pos[i].y-pos[j].y);
}

double fun() {
    double mymin = ((long long int)2)<<60;
    for(int i=0; i<n-1; i++) {
        for(int j=i+1; j<n; j++) {
            double d = mydistance(i,j);
            if(mymin > d) {
                mymin = d;
            }
        }
    }
    return sqrt(mymin);
}

int cmp_xy (const void * a, const void * b) {
    position_t *p1 = (position_t *)a;
    position_t *p2 = (position_t *)b;
    if(p1->x != p2->x) {
        return p1->x - p2->x;
    }
    else {
        return p1->y - p2->y;
    }
}

int cmp_y (const void * a, const void * b) {
    position_t *p1 = (position_t *)a;
    position_t *p2 = (position_t *)b;
    return p1->y - p2->y;
}

double divide(int low, int high) {
    if(low >= high) {
        return ((long long int)2)<<60;
    }
    if(low+1 == high) {
        return mydistance(low, high);
    }
    int mid = (low+high)/2;
    int midx = pos[mid].x;
    double d1 = divide(low, mid);
    double d2 = divide(mid+1, high);
    double mymin = d1>d2 ? d2 : d1;
    
    int k = 0;
    for(int i=low; i<=high; i++) {
        if(abs(midx-pos[i].x) < mymin) {
            tpos[k].x = pos[i].x;
            tpos[k].y = pos[i].y;
            k++;
        }
    }
    qsort(tpos, k, sizeof(position_t), cmp_y);
    for(int i=0; i<k-6; i++) {
        for(int j=1; j<=6; j++) {
            double temp = 1.0*(tpos[i].x-tpos[i+j].x)*(tpos[i].x-tpos[i+j].x) + 1.0*(tpos[i].y-tpos[i+j].y)*(tpos[i].y-tpos[i+j].y);
            if(temp < mymin) {
                mymin = temp;
            }
        }
    }
    
    return mymin;
}

int solve() {
    scanf("%d", &n);
    for(int i=0; i<n; i++) {
        scanf("%d%d", &pos[i].x, &pos[i].y);
    }
    
    //printf("%.2lf\n", fun());
    
    qsort(pos, n, sizeof(position_t), cmp_xy);
    printf("%.2lf\n", sqrt(divide(0, n-1)));
}

int main() {
    int m;
    scanf("%d", &m);
    for(int i=0; i<m; i++) {
        solve();
    }
}

12、寻找凸包(寻找最远点对)

定义: 对一个简单多边形来说,如果给定其边界上或内部的任意两个点,连接这两个点的线段上的所有点都被包含在该多边形的边界上或内部的话,则该多边形为凸多边形

思路:分治法 http://www.myexceptions.net/cpp/2053748.html

https://blog.csdn.net/bone_ace/article/details/46239187

https://www.cnblogs.com/jbelial/archive/2011/08/05/2128625.html

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
typedef struct  {
    int x;
    int y;
    double angle;
    double len;
} position_t;
position_t pos[2001], stack[2001];
int n;
int top;


int cmp_yx(const void* a, const void* b) {
    position_t* p1 = (position_t*)a;
    position_t* p2 = (position_t*)b;
    if (p1->y != p2->y) {
        return p1->y - p2->y;
    }
    else {
        return p1->x - p2->x;
    }
}

int cmp_angle(const void* a, const void* b) {
    position_t* p1 = (position_t*)a;
    position_t* p2 = (position_t*)b;
    if (p1->angle - p2->angle > 0) {
        return 1;
    }
    else if (p1->angle - p2->angle < 0) {
        return -1;
    }
    else {
        if (p1->len - p2->len > 0) {
            return 1;
        }
        else if (p1->len - p2->len < 0) {
            return -1;
        }
        else {
            return 0;
        }
    }
}

int push(int i) {
    stack[top].x = pos[i].x;
    stack[top].y = pos[i].y;
    stack[top].angle = pos[i].angle;
    top++;
    return top;
}

int initstack() {
    top = 0;
}

int pop() {
    top--;
    return top;
}

int isright(int a, int b, int x, int y) {
    if (a * y - b * x > 0) {
        return 0;
    }
    return 1;
}
int canpop(int i) {
    if (top <= 2) {
        return 0;
    }
    if (isright(stack[top - 1].x - stack[top - 2].x, stack[top - 1].y - stack[top - 2].y, pos[i].x - stack[top - 2].x, pos[i].y - stack[top - 2].y)) {
        return 1;
    }
    return 0;
}
int isonline(int a, int b, int x, int y) {
    if (a * y - b * x == 0) {
        return 1;
    }
    return 0;
}

int findp0() {
    int minx = 2147483647;
    int miny = 2147483647;
    int idx = 0;
    for (int i = 0; i < n; i++) {
        if (pos[i].y < miny) {
            miny = pos[i].y;
            idx = i;
        }
    }
    for (int i = 0; i < n; i++) {
        if (pos[i].y == miny && pos[i].x < minx) {
            minx = pos[i].x;
            idx = i;
        }
    }
    return idx;
}

void sortangle() {
    for (int i = 1; i < n; i++) {
        pos[i].len = sqrt((pos[i].y - pos[0].y) * (pos[i].y - pos[0].y) + (pos[i].x - pos[0].x) * (pos[i].x - pos[0].x));
        pos[i].angle = acos((pos[i].x - pos[0].x) / pos[i].len);

    }
    qsort(&pos[1], n - 1, sizeof(position_t), cmp_angle);
}

void removesame() {
    qsort(pos, n, sizeof(position_t), cmp_yx);
    int k = 0;
    for (int i = 1; i < n; i++) {
        if (pos[i].x == pos[k].x && pos[i].y == pos[k].y) {
        }
        else {
            k++;
            pos[k].x = pos[i].x;
            pos[k].y = pos[i].y;
        }
    }
    n = k + 1;
}

int solve() {

    removesame();
    int idx = findp0();

    position_t temppos = pos[idx];
    for (int i = idx; i > 0; i--) {
        pos[i] = pos[i - 1];
    }
    pos[0] = temppos;

    sortangle();

    initstack();
    push(0);
    int k = 2;
    for (; k < n; k++) {
        if (!isonline(pos[k].x - pos[0].x, pos[k].y - pos[0].y, pos[1].x - pos[0].x, pos[1].y - pos[0].y)) {
            break;
        }
    }
    push(k - 1);
    push(k);
    for (int i = k + 1; i < n; i++) {
        while (canpop(i)) {
            pop();
        }
        push(i);
    }

    for (int i = 0; i < top; i++) {
        
        printf("%d %d\n", stack[i].x, stack[i].y);
    }

    return 0;
}

int main() {
    int m;
    scanf("%d", &m);
    for (int i = 0; i < m; i++) {
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            scanf("%d%d", &pos[i].x, &pos[i].y);
        }
        printf("case %d:\n", i + 1);
        solve();
    }
    return 0;
}

13、逆序对

思路:分治法 归并排序思想

https://blog.csdn.net/qq_29796317/article/details/77571942

https://www.cnblogs.com/wanglei5205/p/8893700.html

#include <iostream>  
#include <vector>  

using namespace std;  

//归并排序  
int merge(vector<int> &a, int begin, int mid, int end)  
{  
    int nCount = 0; //前后两段之间逆序的个数  
    vector<int >b;  

    int j = begin, k = mid + 1;  
    int n = end - begin + 1;  
    int i = 0;  
    for (i = 0; i < n ; ++i)  
    {  
        if (j > mid || k > end)  
        {  
            break;  
        }  

        if (a.at(j) <= a.at(k))  
        {  
            b.push_back(a.at(j));  
            ++j;  
        }  
        else  
        {  
            b.push_back(a.at(k));  
            nCount += mid - j + 1;  
            ++k;  
        }  
    }  

    while (j <= mid)  
    {  
        b.push_back(a.at(j));  
        ++j;  
    }  

    while (k <= end)  
    {  
        b.push_back(a.at(k));  
        ++k;  
    }  

    for (i = 0; i < n; ++i)  
    {  
        a[begin + i] = b[i];  
    }  

    return nCount;  
}  

int countReversed(vector<int> &a, int begin,int end)  
{  
    if (begin < end)  
    {  
        int mid = (begin + end) / 2;  
        return (countReversed(a, begin, mid) +  
            countReversed(a, mid + 1, end) + merge(a, begin, mid, end));  
    }  

    else  
    {  
        return 0;  

    }  
}  

int main()  
{  
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		vector<int> arr;  
	    int num = 0;  
	    int n;  
	    cin >> n;  
		//输入数据 
	    for (int i = 0; i < n; ++i)  
	    {  
	        cin >> num;  
	        arr.push_back(num);  
	    }  
	    cout << countReversed(arr, 0, arr.size() - 1) << endl;  
	}
    return 0;  
}  

14、最大子数组和

思路:动态规划

https://www.cnblogs.com/yinianzs/p/9774375.html

#include <iostream>
using namespace std;

int a[50000];

int solve()
{
    int n;
    cin >> n;
    //输入数据
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    //求最大子数组和
    int sum = a[0];
    int max_sum = a[0];
    for (int i = 1; i < n; i++)
    {
        if (sum > 0)
        {
            sum = sum + a[i];
            if (sum > max_sum)
            {
                max_sum = sum;
            }
        }
        else {
            sum = a[i];
            if (sum > max_sum)
            {
                max_sum = sum;
            }
        }
    }
    cout << max_sum << endl;
    return 0;
}
int main()
{
    int m;
    cin >> m;
    for (int i = 0; i < m; i++)
    {
        solve();
    }
    return 0;
}

15、天际轮廓(天际线问题)

思路:归并问题 https://blog.csdn.net/qq_32805671/article/details/84261853

http://itren.xiaolee.net/p/2401100.html

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

struct build_t {
    int a, b, h;
} build[50001];
struct height_t {
    int a, h;
} ht[50001 * 2], temp[50001 * 2];
int n;

int cmp_build(const void* a, const void* b) {
    return ((struct build_t*)a)->a - ((struct build_t*)b)->a;
}
int cmp_height(const void* a, const void* b) {
    struct build_t* c = (struct build_t*)a;
    struct build_t* d = (struct build_t*)b;
    if (c->a != d->a) {
        return c->a - d->a;
    }
    else {
        return c->h - d->h;
    }
}

int remove(int s, int e) {
    int k = s;
    for (int i = s + 1; i < e; i++) {
        if (ht[i].a == ht[k].a) {
            if (ht[k].h < ht[i].h) {
                ht[k] = ht[i];
            }
        }
        else if (ht[k].h != ht[i].h) {
            k++;
            ht[k] = ht[i];
        }
    }
    return k - s + 1;
}

int merge(int low, int lenleft, int mid, int lenright) {
    int i = low;
    int j = mid;
    int k = low;
    int curh = 0;
    int lh = 0;
    int rh = 0;
    for (; i < low + lenleft; i++) {
        for (; j < mid + lenright; j++) {
            if (ht[i].a == ht[j].a) {
                temp[k] = ht[i].h > ht[j].h ? ht[i] : ht[j];
                k++;
                lh = ht[i].h;
                rh = ht[j].h;

                i++;
                if (i >= low + lenleft) {
                    j++;
                    break;
                }
            }
            else if (ht[i].a < ht[j].a) {
                temp[k].a = ht[i].a;
                temp[k].h = ht[i].h > rh ? ht[i].h : rh;
                k++;
                lh = ht[i].h;
                break;
            }
            else {
                temp[k].a = ht[j].a;
                temp[k].h = ht[j].h > lh ? ht[j].h : lh;
                k++;
                rh = ht[j].h;
            }
        }
        if (j >= mid + lenright) {
            break;
        }
    }
    for (; i < low + lenleft; i++) {
        temp[k].a = ht[i].a;
        temp[k].h = ht[i].h;
        k++;
    }
    for (; j < mid + lenright; j++) {
        temp[k].a = ht[j].a;
        temp[k].h = ht[j].h;
        k++;
    }
    for (i = low; i < k; i++) {
        ht[i] = temp[i];
    }
    return remove(low, k);
}

int divide(int low, int high, int depth) {
    if (low > high) {
        return 0;
    }
    if (low == high) {
        ht[2 * low].a = build[low].a;
        ht[2 * low].h = build[low].h;
        ht[2 * low + 1].a = build[low].b;
        ht[2 * low + 1].h = 0;
        return 2;
    }
    int mid = (low + high) / 2;
    int lenleft = divide(low, mid, depth + 1);
    int lenright = divide(mid + 1, high, depth + 1);
    qsort(&ht[2 * low], lenleft, sizeof(struct height_t), cmp_height);
    qsort(&ht[2 * (mid + 1)], lenright, sizeof(struct height_t), cmp_height);

    int res = merge(2 * low, lenleft, 2 * (mid + 1), lenright);

    return res;
}

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d%d%d", &build[i].a, &build[i].b, &build[i].h);
    }
    qsort(build, n, sizeof(struct build_t), cmp_build);
    int len = divide(0, n - 1, 0);
    for (int i = 0; i < len; i++) {
        printf("%d %d\n", ht[i].a, ht[i].h);
    }
    return 0;
}

16、两元素和

思路一:暴力破解 O(n*n) https://www.cnblogs.com/lihuagang/p/leetcode_1.html

思路二:快排+两端扫描 O(nlog n) https://blog.csdn.net/pinkfriday/article/details/62051881
https://blog.csdn.net/Li_zhonglei/article/details/77113017
https://blog.csdn.net/weixin_40547071/article/details/88796451
思路三: 动态规划:https://blog.csdn.net/weixin_42720602/article/details/105807330
https://blog.csdn.net/weixin_40547071/article/details/88796451

#include <iostream>
using namespace std;
int a[50000];

int quickSortPartition(int s[], int l, int r){
    //Swap(s[l], s[(l + r) / 2]); //若以中间数为基准,则先将中间的这个数和第一个数交换即可
    int i = l, j = r, x = s[l]; //将最左元素记录到x中
    while (i < j)
    {
        // 从右向左找第一个<x的数
        // 无需考虑下标越界
        while(i < j && s[j] >= x)
            j--;
        if(i < j)
            s[i++] = s[j]; //直接替换掉最左元素(已在x中存有备份)
        
        // 从左向右找第一个>x的数
        while(i < j && s[i] <= x)
            i++;
        if(i < j)
            //替换掉最右元素(已在最左元素中有备份)
            //最左元素一定被覆盖过,若没有,则表明右侧所有元素都>x,那么算法将终止
            s[j--] = s[i];
    }
    s[i] = x;  //i的位置放了x,所以其左侧都小于x,右侧y都大于x
    return i;
}

void quickSort(int s[], int l, int r)
{
    //数组左界<右界才有意义,否则说明都已排好,直接返回即可
    if (l>=r){
        return;
    }
    
    // 划分,返回基准点位置
    int i = quickSortPartition(s, l, r);
    
    // 递归处理左右两部分,i处为分界点,不用管i了
    quickSort(s, l, i - 1);
    quickSort(s, i + 1, r);
}
void find_key(int arr[], int n, int key)
{
	int begin = 0;
	int end = n - 1;
	while (begin < end)
	{
		if (a[begin] + a[end] < key)
			++begin;
		else if (a[begin] + a[end] > key)
			--end;
		else {
			cout << "yes" << endl;
			break;
		}
	}
	while(begin>=end){
		cout << "no" << endl;
		break;
	}		
}

int solve()
{
	int n,key;
	cin>>n>>key;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	 } 
	quickSort(a,0,n-1);//注意最后一个参数是n-1
    find_key(a,n,key);
}
int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		solve();
	}
    return 0;
}

17、电路布线

思路一:暴力破解 O(n*n) https://blog.csdn.net/weixin_44469806/article/details/109555921

#include <iostream>
using namespace std;
int a[50000];

void connected(int pai[],int n )
{
	int connectMax=0;
	for(int i=0;i<n;i++){
		int t=pai[i];
		for(int j=i+1;j<n;j++){
			if(pai[j]<t){
				connectMax++;
			}
		}
	}
	cout<<connectMax<<endl;
}

int solve()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	connected(a,n);
	
 } 
 
int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		solve();
	}
	
	return 0;
 } 

思路二:实质上求逆序数之和 归并 递归 https://blog.csdn.net/qq_29796317/article/details/77571942

#include<iostream>
#include<vector>

using namespace std;

//计算前后之间逆序对的个数,因为这部分在计算完数组前半部分逆序数和后  
//半部分逆序数之后计算,所以在计算两部分之间的逆序对的同时,对数组进行  
//归并排序  
int merge(vector<int> &a, int begin, int mid, int end)  
{  
    int nCount = 0; //前后两段之间逆序的个数  
    vector<int >b;  

    int j = begin, k = mid + 1;  
    int n = end - begin + 1;  
    int i = 0;  
    for (i = 0; i < n ; ++i)  
    {  
        if (j > mid || k > end)  
        {  
            break;  
        }  

        if (a.at(j) <= a.at(k))  
        {  
            b.push_back(a.at(j));  
            ++j;  
        }  
        else  
        {  
            b.push_back(a.at(k));  
            nCount += mid - j + 1;  
            ++k;  
        }  
    }  

    while (j <= mid)  
    {  
        b.push_back(a.at(j));  
        ++j;  
    }  

    while (k <= end)  
    {  
        b.push_back(a.at(k));  
        ++k;  
    }  

    for (i = 0; i < n; ++i)  
    {  
        a[begin + i] = b[i];  
    }  

    return nCount;  
}  

int countReversed(vector<int> &a, int begin,int end)  
{  
    if (begin < end)  
    {  
        int mid = (begin + end) / 2;  
        return (countReversed(a, begin, mid) +  
            countReversed(a, mid + 1, end) + merge(a, begin, mid, end));  
    }  

    else  
    {  
        return 0;  

    }  
}  


int solve()
{
	int n;
	cin>>n;
	vector<int> arr(n);
	for(int i=0;i<n;i++)
	{
		cin>>arr[i];
	}
	cout<<countReversed(arr, 0, arr.size() - 1) << endl;
	
 } 
 
int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		solve();
	}
	return 0;
}

三、动态规划问题

动态规划学习:https://www.bilibili.com/video/BV18x411V7fm?from=search&seid=5454260386958297352

https://cloud.tencent.com/developer/article/1538177 (推荐)

https://cloud.tencent.com/developer/article/1538940?from=article.detail.1538177 (推荐)

动态规划习题集:https://www.cnblogs.com/cao-lei/p/6891830.html

18、0/1背包问题2 装满的情况

https://blog.csdn.net/iseno_v/article/details/100697105

#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x80000000
//01背包动态规划算法
int KnapSack(int C, int n, int w[], int v[])
{
    int V[n+1][C+1];

    for(int i = 0; i <= n; i++)
    {
        V[i][0] = 0;
    }
    for(int j = 1; j <= C; j++)
    {
        V[0][j] = INF;
    }
    
    //计算第i行,进行第i次迭代
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= C; j++)
        {
            if(j < w[i])
                V[i][j] = V[i-1][j];
            else
                V[i][j] = max(V[i-1][j], V[i-1][j-w[i]]+v[i]);
            if(V[i][j]<0)
            	V[i][j]=INF;
        }
    }
    if(V[n][C]>0)
    	return V[n][C];
    else
    	return 0;
}

int solve()
{
	int N,C;
	cin>>N>>C;
	int v[N];
    int w[N];
	for(int i=1;i<=N;i++)
	{
		//输入每颗宝石的大小 
		cin>>w[i];
		//输入每颗宝石的价值 
		cin>>v[i];
	}
	//输出宝石的最大价值 
	cout<<KnapSack(C,N,w,v)<<endl;
	
}

int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++){
		solve();
	}
	return 0;
}

空间优化方法

#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x80000000

//01背包动态规划算法
int KnapSack(int C, int n, int w[], int v[])
{
    int f[C+1]={0};
    for(int i=1;i<C+1;i++)
    {
    	f[i]=INF;
	}
    //计算第i行,进行第i次迭代
    for (int i = 1; i <= n; i++)
	{
		for (int j = C;j >= w[i] ; j--)
		{
			f[j] = max(f[j], f[j - w[i]] + v[i]);
			if(f[j]<0)
				f[j]=INF; 
		}
		
	}
	if(f[C]>0)
    	return f[C];
    else return 0;
}

int solve()
{
	int N,C;
	cin>>N>>C;
	int v[N];
    int w[N];
	for(int i=1;i<=N;i++)
	{
		//输入每颗宝石的大小 
		cin>>w[i];
		//输入每颗宝石的价值 
		cin>>v[i];
	}
	//输出宝石的最大价值 
	cout<<KnapSack(C,N,w,v)<<endl;
	
}


int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++){
		solve();
	}
	return 0;
}

19、0/1背包问题1

思路:动态规划 https://www.cnblogs.com/gaochaochao/p/9214296.html

https://blog.csdn.net/qq_16467097/article/details/49849959

i代表对i件物体做决策,有两种方式—放入背包和不放入背包 j表示当前背包剩余的容量

#include <iostream>
#include <algorithm>
using namespace std;

//01背包动态规划算法
int KnapSack(int C, int n, int w[], int v[])
{
    int V[n+1][C+1];

    for(int i = 0; i <= n; i++)
    {
        V[i][0] = 0;
    }
    for(int j = 0; j <= C; j++)
    {
        V[0][j] = 0;
    }
    //计算第i行,进行第i次迭代
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= C; j++)
        {
            if(j < w[i])
                V[i][j] = V[i-1][j];
            else
                V[i][j] = max(V[i-1][j], V[i-1][j-w[i]]+v[i]);
        }
    }
    
    return V[n][C];
}

int solve()
{
	int N,C;
	cin>>N>>C;
	int v[N];
    int w[N];
	for(int i=1;i<=N;i++)
	{
		//输入每颗宝石的大小 
		cin>>w[i];
		//输入每颗宝石的价值 
		cin>>v[i];
	}
	//输出宝石的最大价值 
	cout<<KnapSack(C,N,w,v)<<endl;
	
}


int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++){
		solve();
	}
	return 0;
}


一维数组优化空间:https://blog.csdn.net/Iseno_V/article/details/100001133

思路:(1)状态表v的遍历顺序为从第1行开始一行一行遍历,且在遍历第i行时候不会用到第i-2行数据,也就是i-2行及以前的数据没有用了,可以清除。同时,第i-1行的数据每个只会用到一次。

(2)遍历每一行时候只用到当前容量j和j-w[i]的数据,即第 i 次遍历只需要 第 i-1 次遍历中容量小于等于 j 的数据

#include <iostream>
#include <algorithm>
using namespace std;

//01背包动态规划算法
int KnapSack(int C, int n, int w[], int v[])
{
    int f[C+1]={0};
    //计算第i行,进行第i次迭代
    for (int i = 1; i <= n; i++)
	{
		for (int j = C;j >= w[i] ; j--)
		{
			f[j] = max(f[j], f[j - w[i]] + v[i]);
		}
	}
    
    return f[C];
}

int solve()
{
	int N,C;
	cin>>N>>C;
	int v[N];
    int w[N];
	for(int i=1;i<=N;i++)
	{
		//输入每颗宝石的大小 
		cin>>w[i];
		//输入每颗宝石的价值 
		cin>>v[i];
	}
	//输出宝石的最大价值 
	cout<<KnapSack(C,N,w,v)<<endl;
	
}


int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++){
		solve();
	}
	return 0;
}

20、矩阵连乘

思路1:动态规划:https://blog.csdn.net/x_xhuashui/article/details/81903558

https://www.cnblogs.com/xxmmqg/p/12811385.html

注:本地测试用例通过,但上传系统不通过

#include <iostream>
using namespace std;

int a[500];
int p[500];
int m[500][500];

void matrixchain(int n)
{
	for(int i=1;i<=n;i++)
		m[i][i]=0;
	for(int r=2;r<=n;r++)
	{
		for(int i=1;i<=n-r+1;i++)
		{
			int j=i+r-1;
			m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];
			for(int k=i+1;k<j;k++)
			{
				int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
				if(t<m[i][j]){
					m[i][j]=t;
				}
			}
		}
	}
}

int solve()
{
	int N;
	cin>>N;
	for(int i=1;i<=2*N;i++)
	{
		cin>>a[i];
	}
	
	for(int i=0; i<N; i++)
	{
		p[i]=a[2*i+1];
	}
	p[N]=a[2*N];
	matrixchain(N); 
	cout<<m[1][N]<<endl;
	
 } 
 
int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		solve();
	}
	return 0;	
}

思路2:https://blog.csdn.net/wongleetion/article/details/24935617

21、钢条切割

思路:动态规划 https://blog.csdn.net/xy913741894/article/details/71642357

#include<iostream>
using namespace std;

int max(int a, int b)
{
    return a > b ? a : b;
}

int cut(int n, int p[], int result[])
{
    for (int i = 1; i <= n; i++)
    {
        int tempMaxPrice = 0;
        for (int j = 1; j <= i; j++)//下面取得 钢条长度为i的时候的最大收益
        {
            tempMaxPrice = max(tempMaxPrice, p[j] + result[i - j]);
        }
        result[i] = tempMaxPrice;
    }
    return result[n];
}

int solve()
{
    int n; //钢条长度
    int k;
    cin >> n>>k;
    int si[10000] = {0}; //切割钢条长度
    int vi[10000] = {0};  //钢条价值
    for (int i = 0; i < k; i++)
    {
        cin >> si[i];
        cin >> vi[si[i]];
    }
    int result[10000] = { 0 }; //初始化动态规划数组
    cout << cut(n, vi, result) << endl;
}

int main() 
{
    int m;
    cin>>m;
    for(int i=0;i<m;i++)
    {
    	solve();
	}

    return 0;
}


22、最长公共子序列

思路:动态规划 https://blog.csdn.net/Icecoldless/article/details/103696964

https://www.cnblogs.com/lesileqin/p/10322353.html

#include<iostream>
#include<string>
#include<cstring>
#include<stdlib.h>
#define MAX 1001 
using namespace std;
int dp[MAX][MAX];
int main()
{
	int N;
	cin >> N;
	while(N--)
	{
		string a,b;
		cin >> a >> b;
		memset(dp,0,sizeof(dp));
		int len_a=a.size(),len_b=b.size(); 
		for(int i=0;i<len_a;i++)
		{
			for(int j=0;j<len_b;j++)
			{
				if(a.at(i)==b.at(j))
					dp[i+1][j+1]=dp[i][j]+1;
				else 
					dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);
			}
		}
		cout << dp[len_a][len_b] << endl;
		a.clear();
		b.clear();
	}
	return 0;
}

25、最长非降子序列

思路1:同09拦截导弹

思路2:动态规划 https://blog.csdn.net/hustqter/article/details/51200018

#include <iostream>
using namespace std;

int a[10000];

int LIS(int data[], int n)
{
    int *d = new int[n];
    int len = 1;
    for(int i = 0; i < n; i++)
    {
        d[i] = 1;
        for(int j = 0; j < i; j++)
        {
            if(data[j] <= data[i])
            {
                if(d[j] + 1 > d[i])
                {
                    d[i] = d[j] + 1;
                }
            }
        }
        if(d[i]>len) len = d[i];
    }
 
    return len;
}
 
int solve()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	
    cout << LIS(a, n) << endl;
 } 

int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		solve();
	  }  
    
    return 0;
}

26、插入乘号

思路:动态规划 https://blog.csdn.net/kongming_acm/article/details/5903027?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3.control

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

long long dp[20][20];
int sum[20][20];
int a[20];
//dp[i][j]表示(1,i)中有j个乘号 dp[i][j]=max(dp[k][j-1]*sum[k+1][j]);(j<=k<i)

int solve()
{
	int n,k;
    while(scanf("%d%d",&n,&k)==2)
    {
        for(int i=1;i<=n;i++)  scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        {
            for(int j=i;j<=n;j++)
            {
                int cnt=0;
                for(int k=i;k<=j;k++) cnt+=a[k];
                sum[i][j]=cnt;
            }
        }
        for(int i=1;i<=n;i++) dp[i][0]=sum[1][i];
        for(int j=1;j<=k;j++)
        {
            for(int i=j+1;i<=n;i++)
            {
                dp[i][j]=-1;
                for(int k=j;k<i;k++)
                {
                    dp[i][j]=max(dp[i][j],dp[k][j-1]*sum[k+1][i]);
                }
            }
        }
        printf("%lld\n",dp[n][k]);
    }
}

int main()
{
	int m;
	scanf("%d",&m);
	for(int i=0;i<m;i++)
	{
		solve();
	}
    
    return 0;
}

四、贪心算法

贪心策略适用的前提:局部最优策略能导致产生全局最优解

基本思路

  • 建立数学模型来描述问题

  • 把求解的问题分成若干个子问题

  • 对每个子问题求解,得到子问题的局部最优解

  • 把子问题的解局部最优解合成原来问题的一个解

贪心算法的实现框架:

Greedy(C)  //C是问题的输入集合即候选集合
{
    S={ };  //初始解集合为空集
    while (not solution(S))  //集合S没有构成问题的一个解
    {
       x=select(C);    //在候选集合C中做贪心选择
       if feasible(S, x)  //判断集合S中加入x后的解是否可行
          S=S+{x};
          C=C-{x};
    }
   return S;

27、带权活动选择

思路:贪心算法 https://www.cnblogs.com/Jason-Damon/p/3774489.html

https://blog.csdn.net/iteye_9281/article/details/82198686

https://blog.csdn.net/misayaaaaa/article/details/72789998?locationNum=9&fps=1

注:上传系统不通过,具体原因没找出。但本地测试用例都通过,思路是正确的。

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

struct activity{
	int start_time;	//活动开始时间 
	int end_time;	//活动结束时间 
	int v;	//活动权重 
}a[10000];

int GreedyActivitySelect(int st,int fi)
{
	int i,j;
    i = st;
    int sum=a[i].v;
    for (j=i+1; j <= fi; j++)
    {
        if (a[j].start_time >= a[i].end_time)
        {
            i = j;
            sum += a[i].v;
        }
    }
    cout<<sum<<endl;
}

int cmp(const void* a, const void* b)
{
	struct activity* c = (activity*)a;
	struct activity* d = (activity*)b;
	if (c->end_time != d->end_time)
		return (c->end_time - d->end_time);
	if (c->start_time = d->start_time)
		return ((c->v) / (d->end_time) - (d->v) / (d->end_time));
}

int solve()
{
	int n;  //表示n个活动
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i].start_time>>a[i].end_time>>a[i].v;
	 } 
	//对活动按照结束时间和权重比从小到大排序 
	qsort(a, n, sizeof(a[1]), cmp);
	GreedyActivitySelect(1,n);	
}

int main()
{
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		solve();
	}
	return 0;
}

思路:动态规划 https://blog.csdn.net/hongchh/article/details/52914507

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

const int MAX_REQ_NUM = 10000;

struct Request {
  int beginTime;
  int endTime;
  int value;
};

bool operator<(const Request& r1, const Request& r2) {
  return r1.endTime < r2.endTime;
}

class DP {
public:
  void setRequestNum(const int& requestNum) {
    this->requestNum = requestNum;
  }
  void init() {
    for (int i = 1; i <= requestNum; ++i) {
      cin >> reqs[i].beginTime >> reqs[i].endTime >> reqs[i].value;
    }
  }
  // 预备,根据结束时间对所有请求排序,初始化数组p
  void prepare() {
    sort(reqs + 1, reqs + requestNum + 1);
    memset(p, 0, sizeof(p));
    for (int i = 1; i <= requestNum; ++i) {
      for (int j = i-1; j > 0; --j) {
        if (reqs[j].endTime <= reqs[i].beginTime) {
          p[i] = j;
          break;
        }
      }
    }
  }
  // 动态规划算法
  void solve() {
    optimal[0] = 0;
    for (int i = 1; i <= requestNum; ++i) {
      if (optimal[p[i]] + reqs[i].value >= optimal[i-1]) {
        optimal[i] = optimal[p[i]] + reqs[i].value;
      } else {
        optimal[i] = optimal[i-1];
    
      }
    }
  }
  // 输出结果
  void output() {
    cout << optimal[requestNum]<<endl;
  }
private:
  Request reqs[MAX_REQ_NUM + 1];
  int requestNum;
  int p[MAX_REQ_NUM + 1];
  int optimal[MAX_REQ_NUM + 1];
};

int main() {
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		int requestNum;
  		DP dp;
  		cin >> requestNum;
  		dp.setRequestNum(requestNum);
  		dp.init();
  		dp.prepare();
  		dp.solve();
  		dp.output();
	}
  return 0;
}

28、迪杰斯特拉算法

思路:贪心算法 https://blog.csdn.net/catkint/article/details/51224633
https://blog.csdn.net/qq_37334150/article/details/79004785

https://blog.csdn.net/Jayphone17/article/details/101630436

https://blog.csdn.net/i_want_to_studyi/article/details/105080285

https://www.cnblogs.com/fyqq0403/p/10587020.html

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <utility> 
#include <vector>
using namespace std;

const int MAXNV = 500;    // 最大顶点数
const int MAXNE = 10000;    // 最大边数

bool visited[MAXNV];    // visited[i]记录了从源点到顶点i的最短路是否已经求得了。
long long dist[MAXNV];    // dist[i]记录了从源点到顶点i的最短距离,初始值为一较大值。

vector<vector<pair<int, int> > > edge;  // edge[i]:记录顶点i的所有邻接边,pair的第一个元素为终点,第二个元素为权重  

struct myComp
{
    bool operator()(const pair<int, long long> & p1, const pair<int, long long> & p2) const
    {
        return p1.second > p2.second;
    }
};

/* Dijkstra算法求src到dst的最短路径长度 */
long long shortestDist(int src, int dst)
{
    memset(visited, 0, sizeof(bool)*MAXNV);    // 初始化各顶点均为尚未求得最短距离 
    memset(dist, 127, sizeof(long long)*MAXNV);    // 初始化从src到各点的最短距离为很大的值 
    priority_queue<pair<int, long long>, vector<pair<int, long long> >, myComp > q;
    dist[src] = 0; 
    q.push(make_pair(src, 0));
    
    while ( !q.empty() )
    {
        pair<int, long long> p = q.top(); q.pop();
        int v = p.first; long long w = p.second;  
        if ( visited[v] )    // 若已经求得到v的最短距离 
            continue;
        visited[v] = true; 
        dist[v] = w;
        for ( auto it = edge[v].begin(); it != edge[v].end(); ++it )
            if ( !visited[it->first] )
            {
                dist[it->first] = min(dist[it->first], w + it->second);
                q.push(make_pair(it->first, dist[it->first]));
            }
    }
    
    return dist[dst]; 
} 


int solve()
{
	int nv = 0, ne = 0;    // 顶点数,边数
    int src = 0, dst = 0;  // 源点,终点
    scanf("%d %d %d %d", &nv, &ne, &src, &dst);
    
    edge.resize(nv+1);
    
    for ( int i = 0; i < ne; ++i )
    {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        edge[u].push_back(make_pair(v, w));
        edge[v].push_back(make_pair(u, w));
    } 
    
    long long ans = shortestDist(src, dst);
    
    printf("%lld\n", ans);
}

int main()
{
	int m;
	scanf("%d",&m);
	for(int i=0;i<m;i++)
	{
		solve();
	}
	return 0;	
}

29、最小生成树

思路:贪心算法 prim https://blog.csdn.net/qq458291868/article/details/80487751

prim算法和Kruskal算法思想:https://www.cnblogs.com/khbcsu/p/3873428.html

https://www.cnblogs.com/Asimple/p/5551129.html

#include<iostream>
#include<stdlib.h>
#include<string.h> 
using namespace std;
 
typedef struct node
{
    int u, v, w;
}Sj;
 
Sj f[20000];
int q[500];
 
int cmp(const void *a, const void *b)
{
    Sj *aa = (Sj*)a;
    Sj *bb = (Sj*)b;
    return aa->w > bb->w ? 1:-1;
}
 
int cz(int i)
{
    int c = i;
    for(; q[c] >= 0; c = q[c]);
    while(c != i)
    {
        int t = q[i];
        q[i] = c;
        i = t;
    }
    return c;
}
 
void jh(int a, int b)
{
    int f1 = cz(a);
    int f2 = cz(b);
    int t = q[f1] + q[f2];
    if(q[f1] > q[f2])
    {
        q[f1] = f2;
        q[f2] = t;
    }
    else
    {
        q[f2] = f1;
        q[f1] = t;
    }
}
 
int solve()
{
	int n, m;
    cin>>n>>m;
    for(int i = 0; i < m; i++)
        cin>>f[i].u>>f[i].v>>f[i].w;
 
    qsort(f, m, sizeof(f[0]), cmp);
    memset(q, -1, sizeof(q));
 
    int ans = 0;
    int c = 0;
    for(int i = 0; i < m; i++)
    {
        int u = f[i].u;
        int v = f[i].v;
        if(cz(u) != cz(v))
        {
            c++;
            ans += f[i].w;
            jh(u, v);
        }
    }
    if(c == n-1)
        cout<<ans<<endl;
    else
        cout<<"-1"<<endl;
}

int main()
{
    int m;
    cin>>m;
    for(int i=0;i<m;i++)
    {
    	solve();
	}
	return 0;
}

30、黑白连线

思路:假设p[i]=0表示黑点,p[i]=1表示白点,d[i][j]表示从 第i个点到第j个点的距离,则有d[i][j]=0 (p[i]=p[j]); d[i][j]=j-i (p[i]!=p[j])。使用贪心算法,为每个白点加上标志:f[i]=0(p[i]=1)表示该点还没有和黑点连接,若被使用,则更新标志为1。每次找d[i]j的最小值,最后将其值相加。使用栈进行操作。

31、基站布置

思路:https://www.cnblogs.com/titititing/p/9822560.html

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
struct pos_t {
    double x, y;
    double rsect;
} pos[10010];
int cover[10010];
int n;
double d;

const double minINF = 0.00000000001;//浮点误差

int cmp(const void *a, const void *b) {
    pos_t *ta, *tb;
    ta = (pos_t *)a;
    tb = (pos_t *)b;
    double temp = ta->rsect-tb->rsect;
    if(-minINF<=temp && temp<=minINF) {//浮点数比较注意预留一定的精度判断
    //if(temp == 0) {
        return 0;
    }
    else if (temp < 0) {
        return -1;
    }
    else {
        return 1;
    }
}

int solve() {
    scanf("%d%lf", &n, &d);
    for(int i=0; i<n; i++) {
        scanf("%lf%lf", &pos[i].x, &pos[i].y);
        pos[i].rsect = pos[i].x + sqrt(d*d-pos[i].y*pos[i].y);
    }
    memset(cover, 0, sizeof(cover));
    qsort(pos, n, sizeof(pos_t), cmp);
    int count = 0;
    for(int i=0; i<n; i++) {
        if(cover[i] == 1) {
            continue;
        }
        count = count + 1;
        for(int j=i; j<n; j++) {
            if(pos[j].rsect-pos[i].rsect > 2*d) {
                break;
            }
            if(cover[j]==1) {
                continue;
            }
            //下面也需要注意浮点误差
            double temp = (pos[j].x-pos[i].rsect)*(pos[j].x-pos[i].rsect) + pos[j].y*pos[j].y - d*d;
            if(temp<=minINF) {
                cover[j] = 1;
            }
        }
    }
    printf("%d\n", count);
}

int main() {
    int m;
    scanf("%d", &m);
    for(int i=0; i<m; i++) {
        solve();
    }
    return 0;
}

32、岛国难题

https://blog.csdn.net/oneplus123/article/details/83450001

33、机器作业

思路: 贪心算法 https://blog.csdn.net/elrah/article/details/70990825

注:这个代码在实现上没有考虑测试用例的第三种情况,而且时间复杂度O(n*n)

https://blog.csdn.net/ujsDui/article/details/79002893

注:在本地测试没问题,但上传系统超时,具体优化没想好。

#include <iostream>
using namespace std;

int P[50000];	//作业的效益值
int D[50000];	//作业的截止时间 
int J[50000];

int JS(int n)
{
    int r,i;
   	int k = 1;
   	J[1] = 1;
   	for ( i = 2; i <=n; i++)
  	{
    	r = k;
     	while (D[J[r]] > D[i] &&D[J[r]] != r)
        r--;
		if (D[J[r]]<=D[i] && D[i]>r)
        {
            for (int j = k; j > r ; j--)
                J[j + 1] = J[j];
            J[r + 1] = i;
            k++;
        }
    }
	int sum = 0;
	for (int i = 1; i <= k;i++)
    	sum += P[J[i]]; 
    cout<<sum<<endl;
    return 0;
}

void SORT(int start,int end)
{
    for (int i = start+1; i <= end; i++)
    {
        int item = P[i];
        int item_d = D[i];
        int j = i - 1;
        while (j>=start&&item > P[j])
        {
            P[j + 1] = P[j];
            D[j + 1] = D[j];
            j--;
        }
        P[j + 1] = item;
        D[j + 1] = item_d;
    }
}

int solve()
{
	int n;	//表示共有n个作业 
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>D[i]>>P[i];
	}

	SORT(1, n);
	JS(n);

 } 
 
int main()
{
	int T;
	cin>>T;
	for(int i=0;i<T;i++)
	{
		solve(); 
	}
	return 0;
}

思路:可以尝试用动态规划

39、拓扑排序

思路:https://blog.csdn.net/jinzhao1993/article/details/51778468 邻接矩阵

https://blog.csdn.net/lijing805326040/article/details/25076679?utm_source=blogxgwz3

#include<iostream> 
#include<string.h>
#include<malloc.h>
using namespace std;
#define MAXN 300
 
struct ArcNode
{
	int to;
	struct ArcNode *next;
};
 
int n,m; //顶点个数和边数
struct ArcNode * List[MAXN];
int count[MAXN];
char output[100];
 
void TopSort()
{
	int j, i,k,len,top=-1;
	struct ArcNode *temp;
	int bcycle=0;
	int pos=0;
	for(i=0;i<n;i++)
	{
		if(count[i]==0)
		{
			count[i]=top;
			top=i;
		}
	}
	for(i=0;i<n;i++)
	{
		if(top==-1)
		{
			bcycle=1;
			break;
		}
		else
		{
			j=top;
			top=count[j];
			pos+=sprintf(output+pos,"%d",j+1);
			temp=List[j];
 
			while(temp!=NULL)
			{
				k=temp->to;
				if(--count[k]==0)
				{
					count[k]=top;
					top=k;
				}
				temp=temp->next;
			}
		}
	}
	if(bcycle)
		cout<<0<<endl; 
	else
	{
		len=strlen(output);
		output[len]=0;
		for(int i=0;i<len;i++)
		{
			cout<<output[i]<<" ";
		}
		cout<<endl;
	}
}

int solve()
{
	int i,u,v;
	struct ArcNode * temp;
	cin>>n>>m;
	if(n==0&&m==0)
		return 0;
	memset(List,0,sizeof(List));
	memset(count,0,sizeof(count));
	memset(output,0,sizeof(output));
	for(i=0;i<m;i++)
	{
		cin>>u>>v;
		u--;
		v--;
		count[v]++;
		temp=(struct ArcNode *)malloc(sizeof(struct ArcNode));
		temp->to=v;
		temp->next=NULL;
		if(List[u]==NULL)
			List[u]=temp;
		else
		{
			temp->next=List[u]->next;
			List[u]->next=temp;
		}
	}
		TopSort();
		for(i=0;i<n;i++)
		{
			temp=List[i];
			while(temp!=NULL)
			{
				List[i]=temp->next;
				free(temp);
				temp=List[i];
			}
		}
}	 

int main()
{
	int t;
	cin>>t;
	for(int i=0;i<t;i++)
	{
		solve();
	}
	return 0;
}

五、杂项

40、搜索二维矩阵

思路:二分查找 https://blog.csdn.net/L__ear/article/details/86933499

矩阵满足每行元素从左到右升序,每列元素从上到下也是升序。所以对于满足条件的矩阵的所有子矩阵,存在这样一个事实,该矩阵的所有子矩阵左上角第一个元素是子矩阵中最小的元素,右下角最后一个元素是子矩阵中最大的元素。

https://blog.csdn.net/x603560617/article/details/87638942

注:本地测试没问题,但提交系统错误,具体原因没找出。

#include<iostream>
#include<vector> 
using namespace std;

bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty() || matrix[0].empty()) return false;
        int m = matrix.size(), n = matrix[0].size();
        if(target < matrix[0][0] || target > matrix[m-1][n-1]) return false;
        int left = 0, right = m-1;
        while(left <= right)
        {
            int mid = (left + right) / 2;
            if(matrix[mid][0]==target)
                return true;
            else if(target > matrix[mid][0])
                left = mid + 1;
            else right = mid - 1;
        }
        int tmp = right;
        left = 0;
        right = n-1;
        while(left <= right)
        {
            int mid = (left + right) / 2;
            if(matrix[tmp][mid]==target)
                return true;
            else if(target > matrix[tmp][mid])
                left = mid + 1;
            else right = mid - 1;
        }
}

int solve()
{
	//m n  target,分别表示矩阵的行列数以及目标值
	int m,n,target;
	cin>>m>>n>>target;
	
	//输入二维数组
	vector<vector<int>> Matrix;
	vector<int> v;
	int temp=0;
	if(m<=1000 && n<=1000)
	{
		for (int i = 0; i < m; i++)//输入r*c的二维数组
		{
			v.clear();//子数组返回时要清除
			for (int j = 0; j < n; j++)
			{
				cin >> temp;
				v.push_back(temp);
			}
			Matrix.push_back(v);
		}
		int judge;
		judge=searchMatrix(Matrix,target);
		if(judge==1)	cout<<"true"<<endl;
		else cout<<"false"<<endl;
	}
		
 } 
 
int main()
{
	int nums;
	cin>>nums;
	for(int i=0;i<nums;i++)
	{
		solve();
	}
	return 0;
}

41、寻找两个正序数组的中位数

思路:时间复杂度O(log(m+n)) 二分法 找第k小元素

两数组长度为奇数:中位数就是第(m+n)/2+1小元素;两数组长度为偶数:中位数就是第(m+n)/2小与第(m+n)/2+1小元素的和的一半。

https://blog.csdn.net/weixin_42134034/article/details/106157855

为了简化代码,不分情况讨论,我们使用一个小trick,我们分别找第 (m+n+1) / 2 个,和 (m+n+2) / 2 个,然后求其平均值即可,这对奇偶数均适用。加入 m+n 为奇数的话,那么其实 (m+n+1) / 2 和 (m+n+2) / 2 的值相等,相当于两个相同的数字相加再除以2,还是其本身。 如何求两个数组中第k小的元素?

https://blog.csdn.net/mbskypan/article/details/89196429

#include<iostream>
#include<vector>

using namespace std;

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();
        if(m>n){
            nums1.swap(nums2);
            int t = m; m = n; n= t;
        }
        int imin=0,imax=m,halflen=(m+n+1)/2;
        while(imin<=imax){
            int i = (imin+imax)/2;
            int j = halflen-i;
            if(i<imax&&nums2[j-1]>nums1[i])
                imin = i+1;
            else if(i>imin&&nums1[i-1]>nums2[j])
                imax = i-1;
            else{
                int maxLeft = 0;
                if(i==0)
                    maxLeft = nums2[j-1];
                else if(j==0)
                    maxLeft = nums1[i-1];
                else
                    maxLeft = max(nums2[j-1],nums1[i-1]);
                if((m+n)%2==1)  //当m+n为奇数时,中位数是左边的最大值
                    return maxLeft;
                
                int minRight = 0;
                if(i==m)
                    minRight = nums2[j];
                else if(j==n)
                    minRight = nums1[i];
                else
                    minRight = min(nums2[j],nums1[i]);
                
                return (maxLeft+minRight)/2.00000; //除以2.0使得结果为浮点数,如果除以整型2,那么3/2结果将是1,从而导致错误
            }
        }
        return 0.00000;
}

int solve()
{
	int n,m; 
	cin>>n>>m;	
	vector<int> nums1(n);
	vector<int> nums2(m);
	for(int i=0;i<n;i++)
	{
		cin>>nums1[i];
	}
	for(int i=0;i<m;i++)
	{
		cin>>nums2[i];
	}
	
	cout<<findMedianSortedArrays(nums1,nums2)<<endl;
	
 } 
 
int main()
{
	int nums;
	cin>>nums;
	for(int i=0;i<nums;i++)
	{
		solve();
	}
	
	return 0;
}

42、最低票价

思路:动态规划 https://blog.csdn.net/qq_45732909/article/details/105943634

#include<iostream>
#include<vector> 
using namespace std;

int min(int x, int y, int z) 
{
	y = x > y ? y : x;
	return y > z ? z : y;
}

int getdp(int index, vector<int>& dp)
{
	if (index < 0) return 0;

	return dp[index];
}

//如果第i天表旅行,第i天所花费用就是第i-1天所花费用 
int mincostTickets(vector<int>& days, vector<int>& costs)
{
	vector<int> dp(days.back() + 1, 0);
	vector<bool> travel(days.back() + 1, false);
	for (int i = 0; i < days.size(); i++)
		travel[days[i]] = true;

	int n = dp.size();
	for (int i = 1; i < n; i++)
	{
		if (!travel[i])
			dp[i] = dp[i - 1];
		else
			dp[i] = min(getdp(i - 1, dp) + costs[0], getdp(i - 7, dp) + costs[1], getdp(i - 30, dp) + costs[2]);
	}

	return dp.back();
}

int solve()
{
	int m;
	cin>>m;
	vector<int> days(m);
	vector<int> costs(3);
	//输入数据 
	for(int i=0;i<m;i++)
	{
		cin>>days[i];
	}
	for(int i=0;i<3;i++)
	{
		cin>>costs[i];
	}
	
	cout<<mincostTickets(days,costs)<<endl;
		
}
int main()
{
	int nums;
	cin>>nums;
	for(int i=0;i<nums;i++)
	{
		solve();
	}

	return 0;
}

43、鸡蛋掉落

思路:假设取出1个鸡蛋,在X层扔下:

  • 若鸡蛋碎了,则剩余的参数(test_count-1,egg_count-1)能确定X层以下的层。
  • 若鸡蛋没碎,则剩余的参数(test_count-1,egg_count)能确定X层以上的层。
  • 无论扔出的这个鸡蛋什么情况,X层都确定了。

https://blog.csdn.net/qq_41221520/article/details/106005349

#include<iostream>

using namespace std;

int getConfirmFloor(int test_count, int egg_count)
{
    if (test_count == 1 || egg_count == 1)
        return test_count;
    return getConfirmFloor(test_count - 1, egg_count) + getConfirmFloor(test_count - 1, egg_count - 1) + 1;
}

int superEggDrop(int K, int N) {
    int test_count = 1;
    while (getConfirmFloor(test_count, K) < N)
    {
        test_count++;
    }
    return test_count;
}

int solve()
{
	int k,n;
	cin>>k>>n;	//表示有k个鸡蛋,n层楼
	cout<<superEggDrop(k, n)<<endl;
}

int main()
{
	int nums;
	cin>>nums;
	for(int i=0;i<nums;i++)
	{
		solve();
	 } 
	 return 0;
}

:在solve()函数中,由于非void型,所以 最后return 0(如果需要带值出来就return那个值),但在老师那个编译环境,最后写了return 0编译错误,系统不通过,而在Dev编译环境下最后那条语句有没有无所谓,都可编译通过;在VS2019环境下,没有return,编译不通过。当然了solve()函数在例题中基本没有带值出来,直接定义void型,就不存在这个问题了。

posted @ 2021-10-17 22:03  Vinta  阅读(35)  评论(0编辑  收藏  举报