zoukankan      html  css  js  c++  java
  • 算法竞赛进阶指南第一章

    1.位运算

    没啥特别重要的东西,只需要知道有符号的整数是用补码来存的,对补码的每一位取反,则数值上 变成-1.

    好题:求 (a^b) 对p取模的值,其中(1≤a,b,p≤10^{18})
      分析:将 (a^b) 看作b个a相加. b太大怎么办?类似快速幂优化即可.

    2.二分and三分

    主要用于求特定的答案,即这个答案是能够求出来的,而不是问是否存在,有多少个之类的…… 三分法在使用时需要注意:如果在函数中存在一段值相等的部分,三分法就不再适用,事实上出现这种情况也有解决的方法,如果最后确定了左端点l和右端点r,从l到r枚举一遍所有点判断一下答案即可.

    3.平均数问题

    如果解题中出现了n个数的平均数T,将这n个数都减去T,以后与0比较即可. 比如要判断一个区间内的数的平均数是否大于x,那么将这n个数都减去x,然后看操作之后的数的和是否大于0即可.

    4.中位数问题

    如何求中位数?对顶堆!
      经典模型:货仓选址问题.
      上面两个都是书上涉及到的比较简单的处理方法,有些复杂的问题可能会用到减去偏移量与中位数比较等方法.
      减去偏移量这个方法会在以后提到,与中位数比较则是常用的判定中位数是否存在的一种方法. 二分出中位数x,将比二分出的数大的数标为1,小的数标为-1,求序列的最大连续子段和,若大于0,则x可以是原序列的中位数.接着(l = mid + 1),继续二分.

    5.逆序对

    归并排序和树状数组都可以求. 一个比较强大的应用是n*m数码的有解性判定.
      (N×N)的棋盘,N为奇数时,与八数码问题相同。逆序奇偶同性可互达.
      (N)为偶数时,空格每上下移动一次,奇偶性改变。称空格位置所在的行到目标空格所在的行步数为空格的距离(不计左右距离),若两个状态的可相互到达,则有两个状态的逆序奇偶性相同且空格距离为偶数,或者,逆序奇偶性不同且空格距离为奇数数。否则不能。

    6.倍增

    用来加速枚举,替代二分.
      有些题目可以二分,但是在一些极端数据下,二分不如枚举快,这时就可以用倍增来加速枚举,替代二分.举个例子:
      给定一个整数T,求出最大的k,满足(Σa_i≤T(1≤i≤k)), 如果T特别小,序列特别长,那么二分就要一步一步缩回来,而倍增则可以从起点一下子找到答案.

    7.贪心

    收益与代价问题,问题规模一般很大,时间不足以支持dp.
      基本策略:在收益最大的前提下让代价尽量最小. 一般涉及到排序,采取决策包容性大的方式排序, 第i次选择时尽量选当前能选到,下一次不一定能选到的.

    8.习题

    https://ac.nowcoder.com/acm/contest/1004

    poj2965 The Pilots Brothers’ refrigerator

    枚举 / 位运算 / 搜索

    https://www.cnblogs.com/RioTian/p/13461765.html

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int main() {
    	char s[5][5];
    	int mark[5][5];
    	int x[20], y[20];
    	memset(mark, 0, sizeof(mark));
    	for (int i = 0; i < 4; i++) cin >> s[i];
    	for (int i = 0; i < 4; i++) {
    		for (int j = 0; j < 4; j++) {
    			if (s[i][j] == '+') {
    				mark[i][j] = !mark[i][j];//下面的操作中,i,j这个位置翻转了操作了两次,等于没操作,所以上边这里再操作一次,这样让这个位置                                                           也被操作了
    				for (int k = 0; k < 4; k++) {
    					mark[i][k] = !mark[i][k];
    					mark[k][j] = !mark[k][j];
    				}
    			}
    		}
    	}
    	int ans = 0;
    	for (int i = 0; i < 4; i++) {
    		for (int j = 0; j < 4; j++) {
    			if (mark[i][j]) {
    				x[ans] = i + 1;
    				y[ans] = j + 1;
    				ans++;
    			}
    		}
    	}
    	printf("%d
    ", ans);
    	for (int i = 0; i < ans; i++) printf("%d %d
    ", x[i], y[i]);
    	return 0;
    }
    

    poj2083 Fractal

    递归 / 分形

    #include<iostream>
    using namespace std;
    const int N=749;
    int n;
    char a[N][N];
    void solve(int n,int x,int y){
        if(n==1)a[x][y]='X';
        else
            if(n>1){
                int num=1;
                for(int i=2;i<n;i++)
                    num*=3;
                solve(n-1,x,y);
                solve(n-1,x,y+num*2);
                solve(n-1,x+num,y+num);
                solve(n-1,x+num*2,y);
                solve(n-1,x+num*2,y+num*2);
            }
    }
    int main(){
        while(cin>>n&&n!=-1){
           int num=1;
            for(int i=1;i<n;i++)
                num*=3;
            for(int i=0;i<num;i++)
            {
                for(int j=0;j<num;j++)
                    a[i][j]=' ';
                a[i][num]='';
            }
            solve(n,0,0);
            for(int i=0;i<num;i++)
                    printf("%s
    ",a[i]);
            printf("-
    ");
        }
    }
    

    poj3714 Raid

    记录一下是哪种类型的点,然后求不同类型的点的最近点对

    #include<bits/stdc++.h>
    using namespace std;
    typedef long double ld;
    const int N = 1e5 + 10;
    const int inf = 0x3f3f3f3f;
    int n;
    struct node {
    	int x, y;
    	bool z;
    	bool operator < (const node w) const {
    		return x < w.x;
    	}
    }e[N <<1];
    node a[N], b[N];
    
    double s(node x, node y) {
    	if (x.z == y.z) return inf;
    	return sqrt((ld)(x.x - y.x) * (x.x - y.x) + (ld)(x.y - y.y) * (x.y - y.y));
    }
    
    double s(int l, int r) {
    	if (l == r) return inf;
    	if (l + 1 == r) return s(e[l], e[r]);
    	int mid = (l + r) >> 1;
    	return min(s(l, mid), s(mid, r));
    }
    
    int main() {
    	//freopen("in.txt", "r", stdin);
    	ios::sync_with_stdio(false), cin.tie(0);
    	int t; cin >> t;
    	while (t--) {
    		cin >> n;
    		for (int i = 1; i <= n; ++i)cin >> e[i].x >> e[i].y, e[i].z = 0;
    		for (int i = 1; i <= n; ++i)cin >> e[i + n].x >> e[i + n].y, e[i + n].z = 1;
    		sort(e + 1, e + 2 * n + 1);
    		printf("%.3f
    ", s(1, 2 * n));
    	}
    }
    

    poj1723 Soldiers

    排序 / 中位数 / 货仓选址问题拓展

    问题简述

    有N个士兵,每个士兵开始站的坐标是(x,y),现在使得将N个士兵站在同一个水平线(即所有士兵的y坐标相同)并且x坐标相邻,每个士兵每次可以移动一个位置(分别在x和y方向移动)。求出最少的移动步数。

    问题分析

    这是一个最优化问题,可以使用中位数计算来解决。

    x方向和y方向可以分别来考虑。

    y方向就是一个简单的中位数计算。

    x方向是坐标相邻,可以先将x位置排序,然后往中间靠,也是一个中位数计算问题。x方向分别移动到a+i的位置,那么士兵就相邻了,即x[i]=a+i,那么x[i]-i=a。用中位数来算的话,首先将x方向进行排序,然后让x[i]=x[i]-i,再次排序后求中位数即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 10000;
    int x[N], y[N];
    int main() {
        int n; while (cin >> n) {
            for (int i = 0; i < n; i++)
                cin >> x[i] >> y[i];
            sort(x, x + n);
            for (int i = 0; i < n; i++) x[i] -= i;
            sort(x, x + n);
            sort(y, y + n);
            int ans = 0, x_median, y_median;
            if (n % 2 == 1) {
                x_median = x[n / 2];
                y_median = y[n / 2];
            }
            else {
                x_median = (x[n / 2 - 1] + x[n / 2]) / 2;
                y_median = (y[n / 2 - 1] + y[n / 2]) / 2;
            }
            for (int i = 0; i < n; i++)
                ans += abs(x_median - x[i]) + abs(y_median - y[i]);
            cout << ans << endl;
        }
    }
    

    poj1050 To the Max

    贪心 / DP

    题目大意:给你一个n*n的矩阵,要求找出其中的子矩阵,使个元素的和最大,输出最大值。

    题目分析:

    • 分别把第二行加到第一行求最大子段和,再第三行加到第一行求最大子段和,....,第n行...;
    • 再把第三行,第四行,....,n行 依次加到第二行,分别求最大子段和。
    • 记录过程中的最大值即可。
    #include<bits/stdc++.h>
    using namespace std;
    const int N = 100;
    int g[N][N], ans = -114514;
    int main() {
       // freopen("in.txt", "r", stdin);
        ios::sync_with_stdio(false), cin.tie(0);
        int n; scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j)
                scanf("%d", &g[i][j]);
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j)
                g[i][j] += g[i][j - 1];
        for (int i = 1; i <= n; ++i)
            for (int j = i; j <= n; ++j) {
                int sum = 0;
                for (int k = 1; k <= n; ++k) {
                    sum += g[k][j] - g[k][i - 1];
                    ans = max(ans, sum);
                    if (sum < 0) sum = 0;
                }
            }
        printf("%d
    ", ans);
        return 0;
    }
    

    Hdu4864 Task

    贪心

    题目不难,就是读题烦

    分析:贪心题目,比赛的时候想到贪心。是给机器选工作。工作时间跟小于机器最接近的,然后另一个价值yi。不好贪心。

    中间又想到把他们的值放在一个矩阵中贪心,转化为在一个子矩阵中求结果,这个思想也是非常好的,可是大前提没有考虑正确,事实上是给工作选机器。为什么呢?

    由于题目求让完毕的任务最多,所以能够把工作和机器都按时间从大到小,然后价值从大到小,然后给每一个工作找机器。首先全部的工作时间比当前任务的工作时间大的都能够选,我们贪心选择当中价值最小的满足条件的一个机器,把大的留给后面的。这样思路就没有错了、

    然后是处理,假设直接写的话接近O(n^2),必超时,開始想到的优先队列,可是优先队列返回的是最小的。我们要的是首先要满足大于当前任务价值。所以不行,然后能够用vector,也能够直接用一个数组处理。由于时间都是满足条件的,仅仅要贪心选择一个最优的价值。所以能够用一个哈希数组,非常easy的小处理了。

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    struct node{
    	int x,y;
    }ts[101000],mas[101000];
    
    bool cmp(const node &a,const node &b)
    {
    	if(a.x!=b.x)return a.x>b.x;
    	if(a.y!=b.y)return a.y>b.y;
    }
    int book[120];
    int main()
    {
    	int n,m;
    	while(~scanf("%d%d",&n,&m))
    	{
    		for(int i=0;i<n;i++)
    			scanf("%d%d",&mas[i].x,&mas[i].y);
    		for(int i=0;i<m;i++)
    			scanf("%d%d",&ts[i].x,&ts[i].y);
    		sort(mas,mas+n,cmp);
    		sort(ts,ts+m,cmp);
    		memset(book,0,sizeof(book));
    		long long ans=0,count=0;
    		for(int i=0,j=0;i<m;i++)
    		{
    			while(j<n and mas[j].x>=ts[i].x)//满足当前的x,意味着后续的x都可以满足 
    			{
    				book[mas[j].y]++;
    				j++;
    			}
    			for(int k=ts[i].y;k<=100;k++)
    			{
    				if(book[k])
    				{
    					book[k]--;
    					ans++;
    					count+=ts[i].x*500+ts[i].y*2;
    					break;
    				}
    			}
    		}
    		printf("%lld %lld
    ",ans,count);
    	}
    	return 0;
    }
    
  • 相关阅读:
    使用jedis操作redis常用方法
    PostgreSQL下安装pg_stat_statements
    redis+keepalived安装
    eclipse离线安装Activiti Designer插件
    PostgreSQL下安装pg_stat_statements
    mysql设置索引
    使用appium在android7.0真机上测试程序时报错command failed shell “ps ‘uiautomator’”的解决方式
    appium--adb版本过低
    appium报错--版本不匹配
    win7提示不是正版桌面变黑
  • 原文地址:https://www.cnblogs.com/RioTian/p/13460180.html
Copyright © 2011-2022 走看看