刷oj碰到的问题5(180925-)

 

1.FDUCS2018exam-exempted postgraduate test

上机环境:win7-64系统,virtual oj。vs2010,codeblocks,电脑自带vs2010和g++的编译器,测试的时候用g++编译器。这次测试修复了那个刷分的bug,所以就跟各类oj平台保持了一致的输出。。。

There are 3 problems in the exam, and you have 2.5h to solve them. Since there would be lots of students whose major weren't CS, the problems wouldn't be as tough as the problems used to filtrate students of our school.

第一题:共2小问,每一问50分,共100分。

在一个平面直角坐标系中,给定a,b,r,A,B,C的值,从而可以得到表示圆O的方程:(x-a)^2+(y-b)^2 = r^2,及表示直线L的方程:Ax+By+C = 0。

求圆O与直线L的交线段的长度。保留小数点后两位有效数字。

送分题。考察c/c++的double,int 的强制转换,还是sqrt的调用?

第二题:共3小问,30/30/40

给定一个数组A,对于数组中的每个数a,我们记它的好数为:位于a的左侧,且比a大的,离a位置最近的数b的序号index_b。这个数组的序号从1开始计算。如果不存在这样的b,则index_b为0.

求出A数组的对应的好数数组INDEX_B。

动态规划即可。如果左边某个数比a大,则返回它的序号;否则,递归地比较左边这个数的好数对应b的数值和a的大小关系,直到b大于a或者好数已经不存在了。

第三题:共3小问,30/30/40

给定一个数组A和一个目标数T,现在要求数组中所有的序号组合(i,j,k)的个数,满足i<j<k,且A[i]+A[j]+A[k] = T。

我想出的方法(当时编程的时候不知道为啥一直有bug没找到。。。)是:

双指针法,p指向数组首元素,q指向数组尾元素。先对A数组排序,然后用二分搜索去查找从p+1到q-1的元素中,首个值不小于T-A[p]-A[q]的元素的位置x,以及首个大于T-A[p]-A[q]的元素y。然后计算x与y之间,所有值为T-A[p]-A[q]的元素的个数。(在搜索的时候,应该先检测一下,搜索范围内的最小元素与最大元素构成的区间是否涵盖了T-A[p]-A[q]:如果区间都在该值的左侧,则p++;如果都在该值的右侧,则q--;如果p+1>q,则结束运算;如果区间涵盖了该值,则之后先计算在A[p+1]-A[q-1]中,所有和为T-A[p]的组合的个数,再计算在该范围中,所有和为T-A[q]的组合的个数***)。

***部分的算法:用单指针法来计算。时间复杂度估计也在O(nlgn)级别。

所以这题的总时间复杂度为O(n^2*lgn)。

另一种解法貌似是用leetcode的2-sum来做,k-sum问题的时间复杂度一般为O(n^(k-1))。

2.poj 2833堆的应用

#include<cstdio>
#include<queue>
#include<functional>
#include<vector>
using namespace std;
typedef long long LL;

LL sums1,sums2,sums;
int n1, n2, n,dat;
priority_queue<int, vector<int>, less<int> >small_heap;
priority_queue<int, vector<int>, greater<int> >big_heap;

int main()
{
	while (scanf_s("%d%d%d",&n1,&n2,&n) != EOF, n1 || n2 || n)
	{
		sums1 = sums2 = sums = 0;
		for (int i = 0; i < n; ++i)
		{
			scanf_s("%d", &dat);
			sums += dat;
			big_heap.push(dat);
			small_heap.push(dat);
			if (big_heap.size() > n1)
				big_heap.pop();
			if (small_heap.size() > n2)
				small_heap.pop();
		}

		for (int i = 0; i < n1; ++i)
		{
			sums1 += big_heap.top();
			big_heap.pop();
		}
		for (int i = 0; i < n2; ++i)
		{
			sums2 += small_heap.top();
			small_heap.pop();
		}

		printf("%.6lf\n",(sums-sums1-sums2+.0)/(n-n1-n2));
	}

	return 0;
}

3.给定一个长宽均为整数的长方形,要求将其分割为若干个正方形,且这些正方形的边长之和最小。请求出最小边长。

算法:递归即可。

伪代码:

T(a,b)
{
    max_len = max(a,b)
    min_len = min(a,b)
    if(max_len%min_len == 0)
        return max_len
    else
        return max_len-max_len%min_len+T(min_len,max_len%min_len)

4.给定2个长为n的数字字符串,一个为源字符串,另一个为目的字符串(n <= 10)。

给定2个操作:(1)将字符串的某位的数值+1或者-1;(2)将字符串的某2位调换位置。

若要将源字符串经过以上两种操作转换为目的字符串,问最少要几步? 

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

int swap_num(string firs, string sec)
{
	int len = firs.size(),result = 0;
	string fir = firs;
	for (int i = 0; i < len; ++i)
	{
		if (fir[i] != sec[i])
		{
			for (int j = i + 1; j < len; ++j)
			{
				if (fir[j] == sec[i])
				{
					char median = fir[j];
					fir[j] = fir[i];
					fir[i] = median;
					++result;
					break;
				}
			}
		}
	}
	return result;
}

//开始的想法一般为求出n!的,fir的全排列,然后对于每个全排列,求出其到sec的差值;然后进行排序即可求出结果;
//时间复杂度为O(n!)*O(n)+O(nlgn) = O((n+1)!)
int nswap(string fir, string sec)
//当然本算法时间复杂度可以进一步简化,方法是对C(2,n)的位置对(i,j),计算其交换的成本,
//然后找出大于0的最大值并调换该组顺序;注意:因为每次交换只需要移动2位,
//所以之后第i次计算时只需求O(n-i)个变换的值,然后在第i次,求C(n-i+1,2)个元素中的最大值就可以了,(-i是因为每移动一次确定一个位)
//总共要计算O(n^2)个(i,j)对(因为移动至多n-1次);找最大值的复杂度为O(n^2),求的次数最差为n-1;
//所以时间复杂度为O(n^3)
{
	string temp_fir = fir;
	int len = fir.size();
	for (int i = 0; i < len-1; )
	{
		if (fir[i] == sec[i])
			++i;
		else
		{
			while (i < len-1)
			{
				for (int j = i + 1; j < len; ++j)
				{
					int plus_cost = abs(temp_fir[i] - sec[i]);
					int add_cost = abs(temp_fir[j] - sec[j]);
					int swap_plus = abs(temp_fir[i] - sec[j]);
					int swap_add = abs(sec[i] - temp_fir[j]);
					if (plus_cost + add_cost > 1 + swap_plus + swap_add)//need swap
					{
						char median = temp_fir[i];
						temp_fir[i] = temp_fir[j];
						temp_fir[j] = median;
					}
					else if (j == len - 1)
					{
						++i;
						break;
					}
				}
			}
		}
	}
	cout << "the right order of original string is: "<<temp_fir << endl;

	int fir_res = swap_num(temp_fir, fir);
	for (int i = 0; i < len; ++i)
	{
		fir_res += abs(temp_fir[i]-sec[i]);
	}
	return fir_res;

}

int main()
{
	string fir, sec;
	cin >> fir >> sec;
	int res = nswap(fir, sec);
	cout << res << endl;
}

5.leetcode 164桶排序的妙用

用O(n)时间复杂度来计算一个无序数组的两个数的最大差值。

class Solution 
{
public:
	int maximumGap(vector<int>& nums) 
	{
		int res = 0, max_n = -1, min_n = INT_MAX,len = nums.size();
		if (len == 1)
			return 0;
		for (int i = 0; i < len; ++i)
		{
			max_n = max(max_n, nums[i]);
			min_n = min(min_n, nums[i]);
		}
		if (max_n == min_n)
			return 0;
		int gaps = (max_n - min_n) / (len+1)+1;
		vector<int>buttle(len+1, 0);
		vector<int>sta_p(len+1, INT_MAX);
		vector<int>end_p(len+1, -1);
		for (int i = 0; i < len; ++i)
		{
			int ins_p = (nums[i]-min_n) / gaps;
			buttle[ins_p] = 1;
			sta_p[ins_p] = min(sta_p[ins_p], nums[i]);
			end_p[ins_p] = max(end_p[ins_p], nums[i]);
		}

		for (int i = 0; i < len+1; ++i)
		{
			if (buttle[i])
			{
				int temp_sta = end_p[i];
				while (i < len && buttle[i + 1] == 0)
				{
					i++;
				}
				if(i != len)
					res = max(res, sta_p[i+1]-temp_sta);
			}
		}
		return res;
	}
};

6.hdu 4006动态数组的第k大元素

方法就是维护一个优先队列,存储前k大元素,这题的思路在之前已经用过一次了。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<string>
#include<vector>
#include<functional>
using namespace std;

int n, k,temp_d,adder;
string t_order;

int main()
{
	while (cin >> n >> k)
	{
		priority_queue<int, vector<int>, greater<int> >datas;
		for (int i = 0; i < n; ++i)
		{
			cin >> t_order;
			if (t_order == "I")//insert
			{
				cin >> adder;
				if (datas.size() < k)
					datas.push(adder);
				else if(datas.top() < adder)
				{
					datas.pop();
					datas.push(adder);
				}
			}
			else//query
			{
				cout << datas.top() << endl;
			}
		}
	}

	return 0;
}

7.最坏情况为线性时间的选择算法(以poj 2092,hdu1157为例)

hdu1157:

#include<algorithm>
#include<cstdio>
#define NUM 10005
using namespace std;

int datas[NUM],n;

int Select(int a[], int n, int k);
int Partition(int a[], int low, int high, int x) 
{  //这里数组a是[low,high)的,注意右边界最多到a[high-1],
   /*利用x将数组划分为2部分*/
	int i = low;
	high--;
	while (a[i] != x) i++;
	swap(a[low], a[i]);  //将基准移到首位置
	while (low < high) {
		while (low < high&&a[high] >= x) high--;
		a[low] = a[high];
		while (low < high&&a[low] <= x) low++;
		a[high] = a[low];
	}
	a[low] = x;
	return low;
}
int S_sort(int a[], int low, int high, int k) 
{
	int i, j, x, q, n;
	n = high - low;                                
	//n为数组a[low...high]元素个数,注意右边最多取到a[high-1]
	if (n < 5) 
	{      
		//元素小于5时候单独处理
		sort(a + low, a + high);
		return a[low + k - 1];
	}
	for (i = 0; i <n / 5; i++) 
	{
		sort(a + low + i * 5, a + low + i * 5 + 5);   //对每组数据排序
		swap(a[low + i], a[low + i * 5 + 2]);        //中位数移到前面
	}
	x = S_sort(a, low, low + n / 5, n / 10 + 1);          //寻找中位数的中位数、n/10+1非常重要,避免n<10时n/10==0此时会出现问题
	j = Partition(a, low, high, x);             //根据x将数组a划分为2部分,j为x所在数组下标
	q = j - low + 1;                           // q为小于或者等于x元素的个数
	if (q == k)
		return x;
	else if (q>k)
		return S_sort(a, low, j + 1, k);
	else
		return S_sort(a, j + 1, high, k - q);
}
int Select(int a[], int n, int k) 
{
	return S_sort(a, 0, n, k);
}


int main()
{
	while (scanf_s("%d", &n) != EOF)
	{
		for (int i = 0; i < n; ++i)
		{
			scanf_s("%d", datas + i);
		}
		printf("%d\n", Select(datas,n,n/2+1));
	}
	return 0;
}

8.poj 1012 约瑟夫问题

这类问题看似可以用链表来直接求解,不过由于遍历的次数太多,因而当M,N很大的时候不是一种好的做法。所以我们给出一种数学做法:

假设循环数为M,人的编号总是从0开始,记F(N)为剩余N个人的时候,要干掉的人的编号。则F(1) = 0.

F(N) = (F(N-1)+M)%N.

将该式用非递归的代码来实现,即可大幅度降低运算的时间。

事实上,一般解约瑟夫问题都采用了写递归式的方法。这道题的递归式则是:A[i] = (A[i-1]+m-1)%(n-i+1)

#include<cstdio>

//int tables[15],tests[30],k_res;
int k,res_t[14] = { 2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881,13482720 };

int main()
{
	while (scanf_s("%d",&k) != EOF,k)
	{
		printf("%d\n",res_t[k-1]);
	}

	return 0;
}

//int main()
//{
//	for (int i = 1; i < 15; ++i)
//	{
//		int iters = 1;
//		while (iters)
//		{
//			int signs = 1;
//			for (int j = 0; j <= i; ++j)
//			{
//				if (j == 0)
//					tests[j] = 0;
//				else
//					tests[j] = (tests[j - 1] + iters-1) % (2*i-j+1);
//
//				if (j && tests[j] < i)
//				{
//					signs = 0;
//					break;
//				}
//			}
//			if (signs == 1)
//			{
//				tables[i] = iters;
//				printf("%d\n",tables[i]);
//				break;
//			}
//			else
//				iters++;
//		}
//	}
//	return 0;
//}

9.poj 2244 约瑟夫问题

#include<cstdio>

//int tables[155],tests[155];
int n,tt_res[150] = { 0,0,2,5,2,4,3,11,2,3,8,16,4,21,6,5,2,11,20,34,8,15,10,7,13,11,13,45,18,23,8,3,2,25,75,42,13,5,23,13,50,16,18,89,38,8,39,30,29,38,7,45,23,137,46,63,17,48,5,46,34,140,33,39,2,28,29,79,33,48,3,10,46,120,6,37,17,8,44,15,160,20,35,144,104,179,153,24,8,265,19,9,62,7,139,19,44,93,182,27,158,185,193,17,82,3,11,43,55,21,41,146,29,80,59,8,29,66,19,160,59,28,129,127,120,72,45,157,2,63,127,81,318,513,98,28,32,231,236,411,26,45,5,303,228,66,9,205,65,39 };
int main()
{
	while (scanf_s("%d", &n) != EOF,n)
	{
		printf("%d\n",tt_res[n-1]);
	}

	return 0;
}
//int main()
//{
//	for (int i = 3; i <= 150; ++i)
//	{
//		int iters = 2;
//		while (iters)
//		{
//			int signs = 1;
//			tests[0] = 0;
//			for (int j = 1; j <= i; ++j)
//			{
//				tests[j] = (tests[j - 1] + iters) % j;
//			}
//			if (tests[i] == iters % i)
//			{
//				printf("%d,", iters);
//				break;
//			}
//			else
//				iters++;
//		}
//	}
//	return 0;
//}

10.poj 2775 The Number of the Same BST

一开始,我的思路是建立二叉树,然后用回溯法解决这个问题,但是时间上超了(不过,我学到了deque的用法)。

#include<cstdio>
#include<queue>
#define MODS 9901
using namespace std;
typedef long long  LL;

struct Nodes
{
	Nodes*left, *right;
	int val,index;
}nodes[10005];

int N,dat;
LL res;

void traverse(deque<int> &datas)
{
	int len = datas.size();
	if (len == 0)
	{
		res++;
		return;
	}
	int temp_top,sons;
	while (len)
	{
		sons = 0;
		len--;
		temp_top = datas.front();
		datas.pop_front();
		if (nodes[temp_top].left)
		{
			datas.push_back((nodes[temp_top].left)->index);
			sons++;
		}
		if (nodes[temp_top].right)
		{
			datas.push_back((nodes[temp_top].right)->index);
			sons++;
		}
		traverse(datas);
		while (sons--)
		{
			datas.pop_back();
		}
		datas.push_back(temp_top);
	}
	return;
}

int main()
{
	while (scanf_s("%d", &N) != EOF, N)
	{
		res = 0;
		for (int i = 0; i < N; ++i)
		{
			scanf_s("%d",&dat);
			if (!i)//root
			{
				//nodes[i] = {NULL,NULL,dat,0};
				nodes[i].left = nodes[i].right = NULL;
				nodes[i].val = dat;
				nodes[i].index = i;
			}
			else
			{
				Nodes* temp = nodes;
				int par_index,signs;
				while (temp)
				{
					par_index = temp->index;
					if (temp->val >= dat)
					{
						temp = temp->left;
						signs = 0;
					}
					else
					{
						temp = temp->right;
						signs = 1;
					}
				}
				//nodes[i] = { NULL,NULL,dat,i };
				nodes[i].left = nodes[i].right = NULL;
				nodes[i].val = dat;
				nodes[i].index = i;
				if (signs)
					nodes[par_index].right = &nodes[i];
				else
					nodes[par_index].left = &nodes[i];
			}
		}
		deque<int>datas;
		datas.push_front((nodes[0].left)->index);
		datas.push_front((nodes[0].right)->index);
		traverse(datas);
		printf("%lld\n",res%MODS);
	}
}

这道题的另一种思路是:以首个输入点为根节点建二叉排序树,只需要满足父亲节点比出现在相应的孩子节点之前即可。所以可以给出递推公式:

F(a) = F(left[a])\cdot F(right[a]) \cdot C_{LeaveNum[a]-1}^{LeaveNum[max(left[a],right[a])]}

这道题的一个tricky的地方在于求组合数对9901的模。由于组合数的值回非常大,用long long是无法完整记录的,所以我们需要用到一种名叫乘法逆元的办法:

if\quad b\cdot x =1(mod\,M)\\then\quad a\div b=a\div b\cdot (b\cdot x) = a\cdot x(mod\,M)

#include<cstdio>
#include<algorithm>
#define MODS 9901
using namespace std;
typedef long long  LL;

struct Nodes
{
	int left, right,val, index,nums;
}nodes[10005];

int N, dat;
LL res;

void gcd(int x, int y, int &i, int &j)
{
	if (y == 0)
	{
		i = 1, j = 0;
		return;
	}
	gcd(y, x%y, i, j);
	int temp = i; 
	i = j, j = temp - x / y * j;
}

int antim(int x)
{
	int i, j;
	gcd(x, MODS, i, j);
	i = (i%MODS + MODS) % MODS;
	return i;
}

LL combin(int B, int A)
{
	if (A*B == 0)
		return 1;
	int k = 1, i, j = 1;
	for (i = B; i > B - A; i--)
		k = k * i%MODS;
	for (i = 2; i <= A; ++i)
		j = j * i%MODS;
	j = antim(j);
	k = k * j%MODS;
	return k;
}

LL calcul(int root)
{
	if (root <= 0)
		return 1;
	LL lef_com = calcul(nodes[root].left) % MODS,
		rig_com = calcul(nodes[root].right) % MODS;
	LL comb = combin(nodes[root].nums,max(nodes[nodes[root].left].nums,nodes[nodes[root].right].nums)+1)%MODS;
	return lef_com * rig_com%MODS*comb%MODS;
}

int main()
{
	while (scanf_s("%d", &N) != EOF, N)
	{
		memset(nodes, 0, sizeof(nodes[0])*(N+1));
		res = 0;
		for (int i = 1; i <= N; ++i)
		{
			scanf_s("%d", &dat);
			if (i == 1)//root
			{
				nodes[i].val = dat;
				nodes[i].index = i;
			}
			else
			{
				int temp = 1;
				int par_index, signs;
				while (temp)
				{
					nodes[temp].nums += 1;
					par_index = nodes[temp].index;
					if (nodes[temp].val >= dat)
					{
						temp = nodes[temp].left;
						signs = 0;
					}
					else
					{
						temp = nodes[temp].right;
						signs = 1;
					}
				}
				nodes[i].val = dat;
				nodes[i].index = i;
				if (signs)
					nodes[par_index].right = i;
				else
					nodes[par_index].left = i;
			}
		}
		printf("%lld\n", calcul(1));
	}
}

11.poj 3784 最大最小堆

#include<cstdio>
#include<queue>
#include<vector>
#include<functional>
using namespace std;

int set_num,counter,ranks,mid_num,set_len;
int res[10005];

int main()
{
	while (scanf_s("%d", &set_num) != EOF)
	{
		for (int s = 0; s < set_num; ++s)
		{
			counter = 0;
			scanf_s("%d%d", &ranks, &set_len);
			scanf_s("%d", &mid_num);
			res[counter++] = mid_num;
			priority_queue<int, vector<int>, less<int> >left_heap;
			priority_queue<int, vector<int>, greater<int> >right_heap;

			for (int i = 1, fir_d, sec_d; i < set_len; i += 2)
			{
				scanf_s("%d", &fir_d);
				scanf_s("%d", &sec_d);
				if (fir_d <= mid_num && sec_d <= mid_num)
				{
					left_heap.push(fir_d);
					left_heap.push(sec_d);
					right_heap.push(mid_num);
					mid_num = left_heap.top();
					left_heap.pop();
				}
				else if (fir_d > mid_num && sec_d > mid_num)
				{
					right_heap.push(fir_d);
					right_heap.push(sec_d);
					left_heap.push(mid_num);
					mid_num = right_heap.top();
					right_heap.pop();
				}
				else if (fir_d <= mid_num && sec_d > mid_num)
				{
					left_heap.push(fir_d);
					right_heap.push(sec_d);
				}
				else
				{
					left_heap.push(sec_d);
					right_heap.push(fir_d);
				}
				res[counter++] = mid_num;
			}

			int tens = counter / 10;
			printf("%d %d\n",s+1,counter);
			for (int i = 0; i < tens; ++i)
			{
				for (int j = 0; j < 9; ++j)
				{
					printf("%d ", res[i * 10 + j]);
				}
				printf("%d\n", res[i * 10 + 9]);
			}
			for (int i = tens * 10; i < counter; ++i)
			{
				if (i != counter - 1)
					printf("%d ", res[i]);
				else
					printf("%d\n", res[i]);
			}
		}
	}
	return 0;
}

12.leetcode 329求二维数组的最长上升序列

这是好基友头条后端二面的时候碰到的题目,要求15min内做完。确实是很有挑战的一题,在面试的场合只给15min时间太紧张了。不过题目本身并不难,其实就是简单的DFS。。。时间复杂度是O(n^2)。面试的氛围毕竟比较紧张吧,换做是我推免的时候的状态,估计也要30min可能才能搞定。

class Solution {
public:
	int oriented[4][2] = { 0,1,1,0,-1,0,0,-1 };
	int row_len, col_len, res = 0;
	vector<vector<int>>dp, visited;


	int longestIncreasingPath(vector<vector<int>>& matrix)
	{
		row_len = matrix.size();
        if(!row_len)
            return 0;
        col_len = matrix[0].size();
		for (int i = 0; i < row_len; ++i)
		{
			vector<int>temp(col_len, 0);
			dp.push_back(temp);
		}
		for (int i = 0; i < row_len; ++i)
		{
			vector<int>temp(col_len, 0);
			visited.push_back(temp);
		}

		for (int i = 0; i < row_len; ++i)
		{
			for (int j = 0; j < col_len; ++j)
			{
				if (visited[i][j] == 0)
					dfs(matrix, i,j);
			}
		}

		for (int i = 0; i < row_len; ++i)
		{
			for (int j = 0; j < col_len; ++j)
			{
				res = max(dp[i][j], res);
			}
		}
		return res+1;
	}

private:
	int check(int x, int y)
	{
		return (x >= 0 && x < row_len && y >= 0 && y < col_len);
	}

	void dfs(vector<vector<int>>& matrix, int x, int y)
	{
		if (visited[x][y] == 0)
		{
			visited[x][y] = 1;
			for (int i = 0; i < 4; ++i)
			{
				if (check(x + oriented[i][0], y + oriented[i][1]) && matrix[x][y] < matrix[x + oriented[i][0]][y + oriented[i][1]])
				{
					dfs(matrix, x + oriented[i][0], y + oriented[i][1]);
					dp[x][y] = max(dp[x][y], 1 + dp[x + oriented[i][0]][y + oriented[i][1]]);
				}
			}
		}
		else
			return;
	}
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值