刷题日志(4)

写在前面的话:我也是初学,有些分析或知识会有错误,望各位大佬们指教

这里写目录标题

      • 1:SPOJ KATHTHI ( 01BFS )
      • 2:CodeForces 1341E Nastya and Unexpected Guest ( 01BFS )
      • 3:CodeForces 1063B Labyrinth ( 01BFS )
      • 4:CodeForces 1348B Phoenix and Beauty ( 构造 )
      • 5:CodeForces 1348C Phoenix and Distribution( 思维 )
      • 6:POJ 1062 昂贵的聘礼 ( 枚举 + DIJKSTRA )
      • 6:牛客 NC203159 小V和方程 ( 计数dp )
      • 7:CodeForces 1348E Phoenix and Berries (动态规划)
      • 8:CodeForces 1345C ( 思维 )
      • 9:CodeForces 1345D ( 连通块 + 思维 )
      • 10:CodeForces 1350D Orac and Medians ( 思维 )

1:SPOJ KATHTHI ( 01BFS )

原题链接

  • 题意
    nm 列的图,只能上下左右走,如果两个方格字母不相同,花费为1,否则花费为0,问从左上角走到右下角最小花费是多少。
  • 分析:
    01BFS 的裸题,就是说给的图中仅存在权值为 0 和 1 的边,那么可以用双端队列对其进行处理,每次取队首元素,如果走了权值为 0 的边,将其放入队首,否则放入队尾。这样快速得到答案。
  • 代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include
#include
#include
#include
#pragma warning(disable : 4996)
using namespace std;

int n, m, vis[1010][1010];
int w[4][2] = { {1,0},{-1,0},{0,1},{0,-1} };
char g[1010][1010];
struct node {
	int x, y, z;
};

int bfs() {
	deque<node> que;
	node p, temp;
	int i, j;
	for (i = 1; i <= n; i++)
		for (j = 1; j <= m; j++)
			vis[i][j] = -1;
	p.x = p.y = 1;
	p.z = 0;
	que.push_back(p);
	vis[1][1] = 0;
	while (!que.empty())
	{
		p = que.front();
		que.pop_front();
		if (p.x == n && p.y == m)
			return p.z;
		for (i = 0; i < 4; i++)
		{
			int newx = p.x + w[i][0];
			int newy = p.y + w[i][1];
			if (newx <= 0 || newx > n || newy <= 0 || newy > m)
				continue;
			temp = p;
			if (g[newx][newy] != g[p.x][p.y])
				temp.z++;
			if (vis[newx][newy] == -1 || temp.z < vis[newx][newy])
			{
				temp.x = newx;
				temp.y = newy;
				vis[newx][newy] = temp.z;
				if (temp.z - p.z == 0)
					que.push_front(temp);
				else
					que.push_back(temp);
			}
		}
	}
	return -1;
}

int main() {
	int t, i, j;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d", &n, &m);
		for (i = 1; i <= n; i++)
			scanf("%s", g[i] + 1);
		int ans = bfs();
		printf("%d\n", ans);
	}
	system("pause");
	return 0;
}

2:CodeForces 1341E Nastya and Unexpected Guest ( 01BFS )

原题链接

  • 题意:
    m 个坐标,从第 1 个坐标开始要走到第 m 个坐标,期间有绿灯和红灯,分别占用不同时间,红灯时要求一定要在坐标上,每走长度为 1 的距离花费时间 1,只能在坐标上转向,问最后花费时间最短为多少。
  • 分析:
    一开始做的时候识别出来是 BFS 了,但当时还不会 01BFS ,于是用了两种方法做。用第一种做法时理解错题意了,每次走的时候只考虑了转向一次的情况,然后疯狂 WA 。第二种做法是每次只考虑走相邻的坐标,如果绿灯时间刚好用完,标记一下此时红绿灯循环次数,避免重复无用的计算,这种做法似乎可以暴力求出,但是数据量大的时候就会 Memory limit exceeded
    正解应该是转化为边权为 0 和 1 的图来做,用 dp [ i ] [ j ] 记录从开始走到 坐标i 还剩下 绿灯 j 秒 时的最小循环次数,这样只有在绿灯秒数耗尽时会出现权值为 1 的边,将其放进队尾,其他放进队首,最后求解得出。 另外还有一个坑就是题目中给的坐标可能不是按顺序给出。
  • 分析:
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include
#include
#include
#include
#pragma warning(disable : 4996)
using namespace std;

const int N = 10010;
int n, m, land[N], dp[N][1010], g, r, vis[N][1010];
struct node {
	int pos, time;
};

int bfs() {
	deque<node> que;
	node p, temp;
	int ans = -1;
	p.time = 0;
	p.pos = 1;
	que.push_back(p);
	vis[1][0] = 1;
	while (!que.empty())
	{
		p = que.front();
		que.pop_front();
		if (p.time == 0)    //红灯走完,判断可不可以直接到终点
		{
			int re = n - land[p.pos];
			if (re <= g)
			{
				if (ans == -1 || ans > dp[p.pos][0] * (g + r) + re)
					ans = dp[p.pos][0] * (g + r) + re;
			}
		}
		if (p.time == g)    //绿灯走完,循环次数+1,放队尾
		{
			if (vis[p.pos][0] == 0)
			{
				dp[p.pos][0] = dp[p.pos][g] + 1;
				vis[p.pos][0] = 1;
				temp = p;
				temp.time = 0;
				que.push_back(temp);
			}
			continue;
		}
		if (p.pos > 1)
		{
			int cost = land[p.pos] - land[p.pos - 1];
			if (p.time + cost <= g && vis[p.pos - 1][p.time + cost] == 0)
			{
				vis[p.pos - 1][p.time + cost] = 1;
				temp = p;
				temp.pos--;
				temp.time += cost;
				dp[temp.pos][temp.time] = dp[p.pos][p.time];
				que.push_front(temp);
			}
		}
		if (p.pos < m)
		{
			int cost = land[p.pos + 1] - land[p.pos];
			if (p.time + cost <= g && vis[p.pos + 1][p.time + cost] == 0)
			{
				vis[p.pos + 1][p.time + cost] = 1;
				temp = p;
				temp.pos++;
				temp.time += cost;
				dp[temp.pos][temp.time] = dp[p.pos][p.time];
				que.push_front(temp);
			}
		}
	}
	return ans;
}

int main() {
	int i;
	scanf("%d%d", &n, &m);
	for (i = 1; i <= m; i++)
		scanf("%d", &land[i]);
	sort(land + 1, land + 1 + m);
	scanf("%d%d", &g, &r);
	int ans = bfs();
	printf("%d\n", ans);
	system("pause");
	return 0;
}

3:CodeForces 1063B Labyrinth ( 01BFS )

原题链接

  • 题意:
    给一个图和起点,限制向左向右走的次数,有部分点不能走,问最多可以覆盖多少个点。
  • 分析:
    又是 BFS ,然后又是情况分为两种,因此不能按传统的只入队尾的操作了。同样是用 01BFS 的思想,向左向右走因为要消耗次数,所以入队尾,向上向下走入队首,然后 vis 数组记录节点是否被访问过。
  • 代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include
#include
#include
#include
#pragma warning(disable : 4996)
using namespace std;

int n, m, r, c, a, b, cnt;
int w[4][2] = { {1,0},{-1,0},{0,1},{0,-1} };
char s[2010][2010];
int vis[2010][2010];
struct node {
	int x, y, aa, bb;
};

void bfs() {
	deque<node> que;
	node p, temp;
	int i;
	p.x = r;
	p.y = c;
	p.aa = a;
	p.bb = b;
	que.push_back(p);
	cnt = 1;
	vis[p.x][p.y] = 1;
	while (!que.empty())
	{
		p = que.front();
		que.pop_front();
		for (i = 0; i < 4; i++)
		{
			int newx = p.x + w[i][0];
			int newy = p.y + w[i][1];
			if (newx < 0 || newx >= n || newy < 0 || newy >= m || s[newx][newy] == '*' || vis[newx][newy] == 1)
				continue;
			temp = p;
			if (i == 2)
				temp.bb--;
			else if (i == 3)
				temp.aa--;
			if (temp.aa < 0 || temp.bb < 0)
				continue;
			temp.x = newx;
			temp.y = newy;
			vis[newx][newy] = 1;
			cnt++;
			if (i == 2 || i == 3)
				que.push_back(temp);
			else
				que.push_front(temp);
				
		}
	}
}

int main() {
	int i;
	scanf("%d%d", &n, &m);
	scanf("%d%d", &r, &c);
	r--;
	c--;
	scanf("%d%d", &a, &b);
	for (i = 0; i < n; i++)
		scanf("%s", s[i]);
	bfs();
	printf("%d\n", cnt);
	system("pause");
	return 0;
}

4:CodeForces 1348B Phoenix and Beauty ( 构造 )

原题链接

  • 题意:
    给一个长度为 n 的序列,可以往里面插入 1-n 的数字,要求所有长度为 k 的子序列之和都相等,如果不能实现则输出-1.
  • 分析:
    一道 div2B 题都搞了那么久。。。题目已经说明不需要构造出的序列长度最短了,而经过分析可以得出,题目要求构造出长度为 k 的循环序列。
    首先考虑什么情况是不可能的,当原序列中出现不同数字的个数大于 k ,必定无法构造出循环。
    而剩余情况我们可以把所有不同数字储存起来,n和k 都不大于100,对每一个数字位扩展成一个循环,这样长度不会大于题目限制的 10000 。而不同数字出现的个数可能会小于 k ,用 1 补齐。
  • 代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include
#include
#include
#include
#pragma warning(disable : 4996)
using namespace std;

int vis[110], a[110];

int main()
{
	int n, t, k, i, cnt, x, maxn, j;
	vector<int> vec;
	cin >> t;
	while (t--)
	{
		cin >> n >> k;
		vec.clear();
		cnt = 0;
		memset(vis, 0, sizeof(vis));
		for (i = 1; i <= n; i++)
		{
			cin >> x;
			a[i] = x;
			if (vis[x] == 0)
			{
				cnt++;
				vec.push_back(x);
			}
			vis[x]++;
		}
		if (k < cnt)
			printf("-1\n");
		else
		{
			printf("%d\n", k*n);
			int m = -cnt + k;
			for (i = 0; i < n; i++)
			{
				for (j = 0; j < cnt; j++)
				{
					if (i == 0 && j == 0)
						printf("%d", vec[j]);
					else
						printf(" %d", vec[j]);
				}
				for (j = 0; j < m; j++)
					printf(" 1");
			}
			printf("\n");
		}
	}
	system("pause");
	return 0;
}

5:CodeForces 1348C Phoenix and Distribution( 思维 )

原题链接

  • 题意:
    给长度为 n 的字符串,把它分成 k 段,尽量使 k 段之中的字符串字典序最大的那个小。
  • 分析:
    这种分析的题还真不知道如何下手,看了别人的题解真的是简洁明了。
    首先将原来的字符串排序,选取前 k 个字符,如果这其中有不相等的,那么答案已经确定了,因为如果不选这 k 个字符作为首字符,选后面的只会让字符串字典序更大。所以这种情况答案为只输出第 k-1 个字符。
    如果前 k 个字符都相同,那么看剩下的,如果全部都相同,那么平分,最长的为答案。如果不相同,那么把剩余的都加到 s [ k - 1 ] 后面,因为如果加到其他字符后面只会”徒增功耗“。
  • 代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include
#include
#include
#include
#pragma warning(disable : 4996)
using namespace std;

char s[100010];

int main()
{
	int t, i, n, k;
	cin >> t;
	while (t--)
	{
		cin >> n >> k;
		cin >> s;
		sort(s, s + n);
		if (s[k - 1] != s[0])
			cout << s[k - 1];
		else
		{
			cout << s[k - 1];
			if (s[n - 1] == s[k])
			{
				int num = (n - k) / k;
				if ((n - k) % k)
					num++;
				for (i = 0; i < num; i++)
					cout << s[k];
			}
			else
			{
				for (i = k; i < n; i++)
					cout << s[i];
			}
		}
		cout << endl;
	}
	system("pause");
	return 0;
}

6:POJ 1062 昂贵的聘礼 ( 枚举 + DIJKSTRA )

原题链接

  • 题意:
    n 个人售卖东西,需要不同的价钱,对于每样物品可以先得到其他物品再作交换,这样价钱可以便宜一点,而每个人都有等级,在购买东西的时候,所有商家的等级只差不能超过 m
  • 分析:
    本题有两个难点,第一点在建图,把自己当作一个超级源点,到各个商家都有路径,权值为商品的原价。其次因为先得到一些物品之后,对应有的物品可以打折,所以这些关系上也要建边。
    第二个难点是等级的限制上。在跑最短路的时候去记录等级显然不现实,一开始我是在 [ level [ 1 ] - m , level [ 1 ] + m ] 上跑的,显然 WA 了,因为在跑最短路的时候,依然可能会超出限制。正解是因为数据量不大,可以枚举区间,然后求出最小值。
  • 代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include
#include
#include
#include
#pragma warning(disable : 4996)
using namespace std;

typedef long long ll;
const int N = 110;
const int INF = 0x7f7f7f7f;
int m, n, vis[N], p[N], tot, level[N], dis[N];

struct edge {
	int to, val, next;
	edge(int t = 0, int v = 0, int e = 0) :to(t), val(v), next(e) {}
}edges[N << 4];

vector<edge> vec[N];

void addedge(int from, int to, int val) {
	tot++;
	edges[tot].to = to;
	edges[tot].val = val;
	edges[tot].next = p[from];
	p[from] = tot;
}

int Dijkstra(int x, int y) {
	int i, u, v, minn;
	memset(vis, 0, sizeof(vis));
	for (i = 1; i <= n; i++)
		dis[i] = INF;
	for (i = p[n + 1]; i; i = edges[i].next)
	{
		v = edges[i].to;
		if (level[v] < x || level[v] > y)
			continue;
		if (edges[i].val < dis[v])
			dis[v] = edges[i].val;
	}
	for (int k = 1; k <= n; k++)
	{
		minn = INF;
		for (i = 1; i <= n; i++)
		{
			if (vis[i] == 0 && minn > dis[i])
			{
				minn = dis[i];
				u = i;
			}
		}
		vis[u] = 1;
		if (u == 1)
			break;
		for (i = p[u]; i; i = edges[i].next)
		{
			v = edges[i].to;
			if (level[v] < x || level[v] > y)
				continue;
			if (vis[v] == 0 && dis[u] + edges[i].val < dis[v])
				dis[v] = edges[i].val + dis[u];
		}
	}
	return dis[1];
}

int main()
{
	int i, x, y, z, k, j;
	scanf("%d%d", &m, &n);
	for (i = 1; i <= n; i++)
		vec[i].clear();
	memset(p, 0, sizeof(p));
	tot = 0;
	for (i = 1; i <= n; i++)
	{
		scanf("%d%d%d", &z, &level[i], &k);
		addedge(n + 1, i, z);
		for (j = 0; j < k; j++)
		{
			scanf("%d%d", &x, &z);
			vec[i].push_back(edge(x, z, 0));
		}
	}
	for (i = 1; i <= n; i++)
	{
		int len = vec[i].size();
		for (j = 0; j < len; j++)
		{
			if (abs(level[i] - level[vec[i][j].to]) <= m)
				addedge(vec[i][j].to, i, vec[i][j].val);
		}
	}
	int ans = INF;
	for (i = level[1] - m; i <= level[1]; i++)
	{
		ans = min(ans, Dijkstra(i, i + m));
	}
	printf("%d\n", ans);
	system("pause");
	return 0;
}

6:牛客 NC203159 小V和方程 ( 计数dp )

原题链接

  • 题意
    给整数 m ,求有多少种情况使 n 个带根号的数之和等于根号 m
  • 分析:
    非常基本的 计数dp 问题,但是就是没有看出来问题本质。首先要把 m 因式分解,变成 a根号b 的形式。然后转化问题成为 a 个数分配到 n 个位置。确定 dp [ i ] [ j ]j 的 i 划分,那么当 i 个位置都有数时,可以由 j - ii 划分得来。如果有位置为0,那么就是 j 的i - 1 划分
  • 代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include
#include
#include
#include
#pragma warning(disable : 4996)
using namespace std;

typedef long long ll;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
ll dp[1010][1010];

int main()
{
	ll n, m, i, a, j;
	a = 1;
	scanf("%lld%lld", &n, &m);
	for (i = 2; i <= m; i++)
	{
		while (m % (i*i) == 0)
		{
			a *= i;
			m = m / (i*i);
		}	
	}
	//cout << a << endl;
	//dp[i][j]  j的i划分
	dp[0][0] = 1;
	for (i = 1; i <= n; i++)
		for (j = 0; j <= a; j++)
		{
			if (j - i >= 0)
				dp[i][j] = (dp[i - 1][j] + dp[i][j - i]) % mod;
			else
				dp[i][j] = dp[i - 1][j];
		}
	printf("%lld\n", dp[n][a]);
	system("pause");
	return 0;
}

7:CodeForces 1348E Phoenix and Berries (动态规划)

原题链接

  • 题意:
    n 个果园,分别有蓝莓和红莓,现在要进行采摘,规定一个篮子放 k 个,且篮子里只能放相同颜色的,或者是来自相同果园的,问最多能放满多少个篮子。
  • 分析:
    最初的想法是贪心,尽量把相同颜色的放入一个篮子,最后剩下的蓝莓和红莓个数都不会超过 k 个,即最多在这基础上增加一个篮子,然后遍历果园查找有无满足剩下数量的。(最后肯定 WA 了,E题哪有那么简单)。
    正解应该用动态规划。一个果园如果能以混合颜色装下大于两个篮子,其中一种颜色的数量肯定大于等于 k,所以可以通过转换变成剩下一个篮子装混合颜色的。问题转换为到达 i 果园时,采用混合颜色装入篮子,还是统一颜色装入篮子。
    我们要记录到 i 个果园时,剩下红莓和蓝莓的数量,那么数组会开到 dp [ 500 ] [ 500 ] [ 500 ],为了节约空间,只考虑记录红莓,以 dp [ i ] [ j ] 表示到 i 个果园后,剩下 j 个红莓,最大的篮子数量。那么蓝莓数量为 sum - dp [ i - 1 ] [ j ] * k - j
    先考虑混合装,到 i 个果园后,从1开始,枚举取红莓数量p,取蓝莓的数量则为 k - p,然后分别统计此时剩下红莓和蓝莓的数量,分别看是否能装满篮子,计算出 dp [ i ] [ j ]
    最后考虑统一装,仅考虑加上 第 i 个果园的红莓和蓝莓数量后,各自装篮。
  • 代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#pragma warning(disable : 4996)
using namespace std;

typedef long long ll;
struct node {
	int r, b;
}nodes[510];

ll dp[510][510];

int main()
{
	int n, k, i, j, p;
	ll sum = 0;
	memset(dp, -1, sizeof(dp));
	scanf("%d%d", &n, &k);
	for (i = 1; i <= n; i++)
		scanf("%d%d", &nodes[i].r, &nodes[i].b);
	dp[0][0] = 0;
	for (i = 1; i <= n; i++)
	{
		for (j = 0; j < k; j++)
		{
			if (dp[i - 1][j] >= 0)
			{
				int b1 = sum - dp[i - 1][j] * k - j;   //第i个果园未取时,剩下蓝莓数
				for (p = 1; p <= nodes[i].r&&p < k; p++)   //枚举取红莓数
				{
					if (p + nodes[i].b >= k)    //可以混合装篮
					{
						int b2 = b1 + nodes[i].b - (k - p);   //混合装篮后,剩下蓝莓数
						int a2 = j + nodes[i].r - p;          //混合装篮后,剩下红莓数
						dp[i][a2%k] = max(dp[i][a2%k], dp[i - 1][j] + 1 + a2 / k + b2 / k);
					}
				}
				dp[i][(j + nodes[i].r) % k] = max(dp[i][(j + nodes[i].r) % k], dp[i - 1][j] + (j + nodes[i].r) / k + (b1 + nodes[i].b) / k);
			}
		}
		sum += nodes[i].r + nodes[i].b;
	}
	ll ans = 0;
	for (i = 0; i <= n; i++)
		for (j = 0; j <= k; j++)
			ans = max(dp[i][j], ans);
	printf("%lld\n", ans);
	system("pause");
	return 0;
}

8:CodeForces 1345C ( 思维 )

原题链接

  • 题意:
    给一个长度为 n 的数组,每个数 k 的移动规则是 k + a [ k % n ],移动后如果各个位置都有数,一个位置不会出现多个数,那么输出YES,否则输出NO
  • 分析:
    从给出样例中比较容易发现的是,如果 a [ i ]a [ j ] 的差,是 ij 的倍数,那么说不定就可以构造出两个数使得他们移动后位置相同,按照这个思路写了一遍,果然是 WA 了。
    感觉关于模数的题目的做法还是有一定的方法的,就是把下标 i 变成 k*n + i。那么按题目思路就是有 x + a [ x % n ] == y + a [ y % n],转换成 ( k1 * n + i ) - ( k2 * n + j ) == a [ j ] - a [ i ]。再进行变形 ( k1 - k2 ) * n == ( a [ j ] + j ) - ( a [ i ] + i )。所以就是找是否存在两个数 a [ x ] + x mod n 结果相等。
  • 代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#pragma warning(disable : 4996)
using namespace std;

int a[200010], b[200010];

int main()
{
	int t, n, i, j;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &n);
		memset(b, 0, sizeof(b));
		for (i = 0; i < n; i++)
			scanf("%d", &a[i]);
		int flag = 1;
		for (i = 0; i < n; i++)
		{
			int temp = ((a[i] + i) % n + n) % n;
			b[temp]++;
			if (b[temp] > 1)
			{
				flag = 0;
				break;
			}
		}
		if (flag)
			printf("YES\n");
		else
			printf("NO\n");
	}
	system("pause");
	return 0;
}

9:CodeForces 1345D ( 连通块 + 思维 )

原题链接

  • 题意:
    给一个二维数组,在 # 上可以摆放 N ,如果 NS 在同一列或同一行,而不在同一位置,那么 N 会向 S 移动。问是否能找到最少数量的 N ,满足以下情况:每一行和每一列都有 S,存在N 走到每一个 # 上,但 N 不能走到 . 上。
  • 分析:
    这题读起来比较晦涩难懂,但是有一个很重要的点要抓住,那就是每一行和列都要有 S,这就意味着处在这一行或列的 N 都会朝该 S 移动。可以推出如果同一行或列有两个 # ,而他们之间有相隔,那么必定不满足情况。
    那么如何求最小数量呢?这一个比较容易处理,就是求二维数组中连通块的数量,用 dfs 解决。
    另外一点是 S 可以摆放在任意位置,所以才会有题目给出最后样例的情况,所以我们要统计每一行和列里 # 的出现次数,如果该行(列)没有 # ,那么要手动添加一个 S ,但是又不能影响对应的列(行),所以选的位置要满足该行和该列都没有 #
  • 代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#pragma warning(disable : 4996)
using namespace std;

char s[1010][1010];
int n, m, g[1010][1010], tot;
int row[1010], col[1010];
int w[4][2] = { {1,0},{-1,0},{0,1},{0,-1} };

void dfs(int x, int y, int num) {
	g[x][y] = num;
	int i;
	for (i = 0; i < 4; i++)
	{
		int newx = x + w[i][0];
		int newy = y + w[i][1];
		if (newx<1 || newx>n || newy<1 || newy>m || s[newx][newy] == '.' || g[newx][newy] != 0)
			continue;
		dfs(newx, newy, num);
	}
}

int main()
{
	int i, j, flag, num;
	scanf("%d%d", &n, &m);
	for (i = 1; i <= n; i++)
		scanf("%s", s[i] + 1);
	tot = 0;
	for (i = 1; i <= n; i++)
		for (j = 1; j <= m; j++)
		{
			if (s[i][j] == '#')
			{
				row[i]++;
				col[j]++;
			}
			if (s[i][j] == '#'&&g[i][j] == 0)
			{
				tot++;
				dfs(i, j, tot);
			}
		}
	flag = 1;
	for (i = 1; i <= n; i++)
	{
		num = 0;
		for (j = 1; j <= m; j++)
		{
			if (j == 1 && g[i][j] != 0)
				num++;
			else if (j > 1 && g[i][j] != 0 && g[i][j - 1] != g[i][j] && num > 0)
			{
				flag = 0;
				break;
			}
			else if (g[i][j] != 0 && num == 0)
				num++;
		}
		if (flag == 0)
			break;
	}
	if (flag)
	{
		for (j = 1; j <= m; j++)
		{
			num = 0;
			for (i = 1; i <= n; i++)
			{
				if (i == 1 && g[i][j] != 0)
					num++;
				else if (i > 1 && g[i][j] != 0 && g[i - 1][j] != g[i][j] && num > 0)
				{
					flag = 0;
					break;
				}
				else if (g[i][j] != 0 && num == 0)
					num++;

			}
			if (flag == 0)
				break;
		}
	}
	if (flag)
	{
		for (i = 1; i <= n; i++)
		{
			if (row[i] == 0)
			{
				num = 0;
				for (j = 1; j <= m; j++)
				{
					if (col[j] == 0)
					{
						num++;
						break;
					}
				}
				if (num == 0)
				{
					flag = 0;
					break;
				}
			}
		}
		for (j = 1; j <= m; j++)
		{
			if (col[j] == 0)
			{
				num = 0;
				for (i = 1; i <= n; i++)
				{
					if (row[i] == 0)
					{
						num++;
						break;
					}
				}
				if (num == 0)
				{
					flag = 0;
					break;
				}
			}
		}
	}
	if (flag)
		printf("%d\n", tot);
	else
		printf("-1\n");
	system("pause");
	return 0;
}

10:CodeForces 1350D Orac and Medians ( 思维 )

原题链接

  • 题意:
    n 个数,可以进行一个操作,选定一个区间,然后把这个区间的数都变为中位数,问是否可以使 n 个数都变成 k
  • 分析:
    首先如果该序列不存在 k ,那么就肯定输出 no。然后我一开始的想法是找到值为 k 的地方,如果相邻的数比它大,那么可以选择长度为2的区间,将该区间变为 k,然后再选择长度为3的区间,那么就一定可以把所有的数都变成 k 了。但是如果相邻的数都比它小怎么办?这个就很难继续求解下去了,只能通过暴力搜索的方法,但显然不可取。
    关键是我只关心了把数变为 k,而忽略了可以把数变为其他值,按照上述的想法,只要我们把值为 k 的数的两边其中一个数变成比他大的数就可以了,那么我们考虑把区间都变为比 k 大的数,显然只要长度为3的区间里,存在2个大于等于 k 的数,就可以把整个区间变成比 k 大的数,然后再进行相关操作把所有数变为 k
  • 代码:
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#pragma warning(disable : 4996)
using namespace std;

int f[100010];

int main() {
    int t, n, i, j, flag, k, m;
    cin >> t;
    while (t--)
    {
        cin >> n >> k;
        flag = 0;
        for (i = 1; i <= n; i++)
        {
            cin >> m;
            if (m > k)
                f[i] = 2;
            else if (m == k)
            {
                f[i] = 1;
                flag++;
            }
            else
                f[i] = 0;
        }
        if (flag == 0)
            cout << "no" << endl;
        else if (flag == n)
            cout << "yes" << endl;
        else
        {
            flag = 0;
            int sum = 0;
            for (i = 1; i <= n; i++)
            {
                for (j = i + 1; j - i <= 2 && j <= n; j++)
                {
                    if (f[i] && f[j])
                    {
                        flag = 1;
                        break;
                    }
                }
            }
            if (flag)
                cout << "yes" << endl;
            else
                cout << "no" << endl;
        }
    }
    system("pause");
    return 0;
}

你可能感兴趣的:(刷题日志(4))