2019 360校招笔试- 编程题 -2018.08.27

解题思路:

求出最大的横坐标差和最大的纵坐标轴之差,取较大值作为正方形城市边长即可。

代码:

#include <iostream>
#include <cmath>
using namespace std;
const int INF=1e9+5;

int main()
{
    int n;
    while(cin >> n)
    {
        int min_x=INF, max_x=-INF, min_y=INF, max_y=-INF;
        int x, y;
        for(int i=0; i<n; i++)
        {
            cin >> x >> y;
            if(x > max_x)
                max_x = x;
            if(y > max_y)
                max_y = y;
            if(x < min_x)
                min_x = x;
            if(y < min_y)
                min_y = y;
        }
        //坐标取值范围[-1e9, 1e9],面积可能大于int范围。
        long long tmp = max(max_x-min_x,max_y-min_y); 
        cout << tmp*tmp << endl;
    }
    return 0;
}

 



解题思路:

解法1:离线+树状数组。先把询问离线,并且按照右端点排序,然后从小区间开始,然后树状数组的含义就是指以当前r为结尾的前缀区间(除去后面出现过的元素)的元素种类数,简单点说,当计算到l , r区间,把l - r区间还没在树状数组上更新的值,更新一遍,在之前已经存在了的值先删掉再更新一遍,确保我确定的元素都是往r靠的。

解法2:主席树。

解法3:暴力求解。维护一个大小为m的数组,记录每个数的前缀出现次数,把l - r区间的每个数相减,再统计出现次数大于0的数字总数即为答案。(时间复杂度为Q*m,只适用于Q*m较小时;若此题m的范围改为1~1000,则超时。)

(在解法1和解法2中,m均用不上)

代码(解法1):

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

const int N = 2005;
const int M = 1000005;
struct node
{
	int x, y, id;
}b[M];

int c[N], ans[M], a[N];
map<int, int >vis;

bool cmp(node a, node b)
{
	return a.y<b.y;
}
int lowbit(int x)
{
	return x & (-x);
}
void update(int x, int val,int n)
{
	while (x <= n)
	{
		c[x] += val;
		x += lowbit(x);
	}
}
int sum(int x)
{
	int s = 0;
	while (x)
	{
		s += c[x];
		x -= lowbit(x);
	}
	return s;
}
int main()
{
	int n, m, k;
	while (~scanf("%d%d", &n, &k))
	{
		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i]);

		scanf("%d", &m);
		for (int i = 0; i<m; i++)
		{
			scanf("%d%d", &b[i].x, &b[i].y);
			b[i].id = i;
		}

        	cout<<endl;
		sort(b, b + m, cmp); 
        	for (int k = 0; k<m; k++)
        	{
         	   cout<<b[k].x<<" "<<b[k].y<<endl;
        	}
        	cout<<endl;

		int pre = 1;
		memset(c, 0, sizeof(c));
        	vis.clear();
		for (int i = 0; i<m; i++)
		{
			for (int j = pre; j <= b[i].y; j++)
			{
				if (vis.find(a[j]) == vis.end())
					update(j, 1, n);
				else
				{
					update(vis[a[j]], -1, n);
					update(j, 1, n);
				}
				vis[a[j]] = j;
			}

			ans[b[i].id] = sum(b[i].y) - sum(b[i].x - 1);
			pre = b[i].y + 1;
		}
      
       
        	cout<<endl;
		for (int i = 0; i<m; i++)
			printf("%d\n", ans[i]);
	}
	return 0;
}

 

 


解题思路:

很明显这是最长公共子序列LCS.

但是由于数据太大,普通的O(n2)的做法肯定不行,我们要考虑一种更快的做法.把LCS问题转换成LIS问题,然后再利用LISnlogn解法来求出答案。

思路是这样的,首先假设有两个串,为ab,比如:

a12354
b54321
下标12345

我们找出a中的每个数在b中的下标。

1出现在5位置,2出现在4位置,3出现在3位置,5出现在1位置,4出现在2位置,那么就形成了一个新的串.

5 4 3 1 2

(注意:若a中的元素在b中当有多个位置(k1,k2,k3……)时,要降序排列(k1>k2>k3),这样求最长上升子序列的时候,可以保证从这些位置中只取出一个)

然后对新生成的串求出LIS长度就是答案

注:LCS在最终的时间复杂度上不是严格的O(nlogn),不知均摊上是不是。
       举个退化的例子:
       如    a:666                b:66666

       则新的序列为432143214321
       长度变成了n*m ,最终时间复杂度O(n*m*(lognm)) > O(n*m)。

 

不过此题每个元素都不一样,则算法时间复杂度O(nlogn)

代码:

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

const int MAXN = 50005;

int a[MAXN], lis[MAXN * 20], d[MAXN * 20];
vector<int> pos[MAXN];

int main() 
{
	int n;
    while(~scanf("%d", &n))
	{
        for (int i = 1; i <= n; i++)
		    scanf("%d", a + i);

        int b;
	    for (int i = 1; i <= n; i++) 
	    {
		    scanf("%d", &b);
		    pos[b].push_back(i);
	    }

	    int len_lis = 1;
	    for (int i = 0; i <= n; i++)
		    for (int k = pos[a[i]].size() - 1; k >= 0; k--)
			    lis[len_lis++] = pos[a[i]][k];

	    d[1] = lis[1];
	    int max_len_lcs = 1;
	    for (int i = 2; i <= len_lis; i++)
	    {
		    if (lis[i] > d[max_len_lcs])
			    d[++max_len_lcs] = lis[i];
		    else
		    {
			    int pos_greater_than_lis_i = lower_bound(d, d + max_len_lcs, lis[i]) - d;
			    d[pos_greater_than_lis_i] = lis[i];
		    }
	    }

	    printf("%d\n", max_len_lcs);
    }
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值