题目地址: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;
}