Codeforces Educational Codeforces Round 5

题目地址:http://codeforces.com/contest/616


CodeForces 616A
题意:输入两个数字【最大为10^6位的非负整数】,判断大小,可能有前导0
思路:模拟,先分别去掉前导0,然后判断位数,位数不相等的话,位数大的比较大位数相等的话,逐字符比较大小即可。

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<stdlib.h>
using namespace std;
const int MAXN = 1000005;
#define INF 0x3f3f3f3f
char a[MAXN], b[MAXN];
char cmp()
{
	char *p = a;
	char *q = b;
	while (*p == '0'&&p != '\0')
	{
		p++;
	}
	while (*q == '0'&&q != '\0')
	{
		q++;
	}
	int lena = strlen(p);
	int lenb = strlen(q);
	if (lena != lenb)
	{
		return (lena > lenb ? '>' : '<');
	}
	else
	{
		while (1)
		{
			if (*p == '\0'&&*q == '\0')
			{
				return '=';
			}
			if (*p > *q)
			{
				return '>';
			}
			else if (*p < *q)
			{
				return '<';
			}
			p++; q++;
		}
	}
}
int main()
{
	while (scanf("%s", a) != EOF)
	{
		scanf("%s", b);
		char ans = cmp();
		printf("%c\n", ans);
	}
	return 0;
}

CodeForces - 616B
题意:给定一个矩阵,c[i][j]代表去这件餐厅吃饭的花费,有两个人一起去吃饭。一个人选择行【先选】,并且希望去尽量贵的餐厅,一个人选择列,希望去尽量便宜的餐厅吃饭。问两个人最终选择哪间餐厅吃饭,输出这间餐厅的花费。
思路:贪心,每一行的最小值中的最大值。

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<stdlib.h>
using namespace std;
const int MAXN = 105;
#define INF 0x3f3f3f3f
int restaurant[MAXN][MAXN];
int main()
{
	int n, m;
	while (scanf("%d%d",&n,&m)!=EOF)
	{
		int ans = -1;
		for (int i = 0; i < n; i++)
		{
			int temp = INF;
			for (int j = 0; j < m; j++)
			{
				scanf("%d", &restaurant[i][j]);
				temp = min(temp, restaurant[i][j]);
			}
			ans = max(ans, temp);
		}
		printf("%d\n", ans);
	}
	return 0;
}

CodeForces - 616C

题意:给定一个n*m大小的矩阵,'.'代表空,'*'代表阻碍。输出结果为:对于原矩阵'.'的地方还是输出'.',对于原矩阵'*'的地方,输出以当前'*'的坐标【并且把当前的'*'改为'.'其他地方不变】的链通块的'.'的数目【结果对10求余】。
思路:因为图的大小为?1000*?1000,所以不可能对于每个'*'都进行一次dfs求连通点的数目,因为要对于每个'*'求连通点的数目,所以我们可以预先求出原图里以每个'.'为中心的连通数目,然后对于输出的每个'*'分别加上他四周围的连通数目就是结果了【注意可能四周连通块不一定就是四个不相交的连通块,也可能是有交集的连通块,即有可能重复的累加在一起了,所以可以用并查集来解决这个麻烦,对于预处理dfs的每个'.',以第一次递归的'.'为根,其余为孩子,然后对于每个'*',如果四周是'.'即累加对应'.'的根的树的大小【即连通块大小】,并标记此根已经累加过,这样就可以解决重复累加的麻烦,因为是二维。所以可能把每个二维的点映射到一个数字。

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<stdlib.h>
using namespace std;
const int MAXN = 1005;
#define INF 0x3f3f3f3f
char cell[MAXN][MAXN],ans[MAXN][MAXN];//原矩阵,结果矩阵
int path[MAXN*MAXN],size[MAXN*MAXN],vis[MAXN][MAXN];
//并查集根数组,大小数组,DFS的标记数组
int n,m,dist[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1 };
//矩阵规模,方向向量
bool root_vis[MAXN*MAXN];
//根标记数组
int find(int x)
{//并查集:找根节点
	return x == path[x] ? x : path[x] = find(path[x]);
}
void Union(int x, int y)
{//并查集:合并2个子集,路径压缩
	int rootx = find(x);
	int rooty = find(y);
	if (rootx == rooty)
	{
		return;
	}
	if (size[rootx] > size[rooty])
	{
		size[rootx] += size[rooty];
		path[rooty] = rootx;
	}
	else
	{
		size[rooty] += size[rootx];
		path[rootx] = rooty;
	}
}
bool check(int x, int y)
{//坐标是否越界
	if (x >= 0 && x < n&&y >= 0 && y < m)
	{
		return true;
	}
	else
	{
		return false;
	}
}
void dfs(int x, int y)
{//dfs求连通块
	vis[x][y] = 1;
	int nextx, nexty;
	for (int i = 0; i < 4; i++)
	{
		nextx = x + dist[i][0];
		nexty = y + dist[i][1];
		if (check(nextx,nexty)&&!vis[nextx][nexty] && cell[nextx][nexty] == '.')
		{
			dfs(nextx, nexty);
			Union(x*m + y, nextx*m + nexty);//以当前为根,其他下一级递归为孩子合并树
		}
	}
}
void init()
{//初始化
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			vis[i][j] = 0;
			path[i*m + j] = i*m + j;
			if (cell[i][j] == '.')
			{
				size[i*m + j] = 1;
			}
			else
			{
				size[i*m + j] = 0;
			}
		}
	}
}
int main()
{
	while (scanf("%d%d",&n,&m)!=EOF)
	{
		for (int i = 0; i < n; i++)
		{
			scanf("%s", &cell[i]);
		}
		init();
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				if (cell[i][j] == '.'&&!vis[i][j])//预处理求每个'.'的连通块大小
				{
					dfs(i, j);
				}
			}
		}
		memset(root_vis, false, sizeof(root_vis));//初始化所以根未累加过
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				if (cell[i][j] == '.')//原矩阵为'.',答案也是'.'
				{
					ans[i][j] = '.';
				}
				else//原矩阵为'*'
				{
					int num = 1,nextx,nexty;
					//连通块大小,'*'四周的坐标
					for (int k = 0; k < 4; k++)
					{
						nextx = i + dist[k][0];
						nexty = j + dist[k][1];
						if (check(nextx,nexty)&&cell[nextx][nexty]=='.')
						{//没越界并且原图对应是'.'
							int root_xy = find(nextx*m + nexty);//找到四周'.'对应的根
							if (root_vis[root_xy] == false)//如果未累加过
							{
								num += size[root_xy];//累加
								root_vis[root_xy] = true;//标记已经累加过
							}
						}
					}
					ans[i][j] = (num % 10) + '0';//结果为大小求余10
					for (int k = 0; k < 4; k++)//注意!对于标记的恢复!
					{
						nextx = i + dist[k][0];
						nexty = j + dist[k][1];
						if (check(nextx, nexty) && cell[nextx][nexty] == '.')
						{
							int root_xy = find(nextx*m + nexty);
							root_vis[root_xy] = false;//恢复
						}
					}
				}
			}
		}
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				printf("%c", ans[i][j]);
			}
			printf("\n");
		}
	}
	return 0;
}

CodeForces 616D
题意:给定n和k,n代表有n个数,然后求出一个最长的子序列要求子序列里面不同的数字不能超过k个,输出这个子序列的开始和结束。
思路:滑动窗口,分别用2个下标代表左端点和右端点,当左端点到右端点里面的数字满足要求时,右端点不断往右移动一位,直到不满足要求【即不同的数大于k个】那么当前对应的序列长度为:(右端点-1【因为当前的右端点已经不满足要求了,但是右端点-1还是满足要求的】)-左端点+1,然后看长度大就更新。注意下边界的处理即可。

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<stdlib.h>
using namespace std;
const int MAXN = 500005;
const int MAXM = 1000005;
#define INF 0x3f3f3f3f
int num[MAXN], vis[MAXM];
int main()
{
	int n, k;
	while (scanf("%d%d",&n,&k)!=EOF)
	{
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &num[i]);
		}
		int l, r, len=-1;//结果
		int ll = 1, rr = 1,cont=0;//左下标和右下标,不同数组的个数
		memset(vis, 0, sizeof(vis));//初始化数字都没出现过
		while (rr <= n)//检查n个数
		{
			if (vis[num[rr]] == 0)//没出现过
			{
				cont++;//个数+1
			}
			vis[num[rr]]++;//数字出现+1
			if (cont > k)//已经超出要求,但是(rr-1)还是满足要求的!
			{
				if (rr - ll > len)//是否比之前的结果要长
				{
					len = rr - ll;//也可以写成len=(rr-1)-ll+1
					r = rr-1; l = ll;//保存当前最优的左下标和右下标
					//注意满足要求的右下标是rr-1,不是rr
				}
				while (cont > k)//左下标右移,直到再次满足要求为止
				{
					vis[num[ll]]--;//出现次数-1
					if (vis[num[ll]] == 0)//已经减光了,即当前的ll,rr已经没这个数组了
					{
						cont--;//个数-1
					}
					ll++;//左下标右移
				}
			}
			rr++;//右下标右移
			if (rr == n+1)//到达最后一个数
			{
				if (rr - ll > len)
				{
					len = rr - ll;
					r = rr - 1; l = ll;
				}
			}
		}
		printf("%d %d\n", l, r);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值