codeforces Round #775 div2 C题题解

今天突然心血来潮,想到之前欠下了很多cf的题,就借这个机会写一篇博客。

题目见codeforces #775 div2 c题:Problem - C - Codeforces

 

题目大致意思就是说有一个棋盘,格子被涂上了不同的颜色,然后说了个曼哈顿距离,其实就是横纵坐标差的绝对值求和。现在要输出的就是所有颜色相同的格子的曼哈顿距离之和。

我之前做的时候呢,就想都没想用bfs搜索写的(汗。。。),

#include<bits/stdc++.h>
using namespace std;
int n,m;
int Map[1000][1000];
int vis[1000][1000];
int sum=0;
struct node
{
	int x;
	int y;
	int steps;
};
bool ok(int x,int y)
{
	if(x<0||x>=n||y<0||y>=m)
		return false;
	else
		return true;
}
void bfs(int num,int i,int j)
{
	struct node cur,nex;
	cur.x=i;
	cur.y=j;
	cur.steps=0;
	vis[cur.x][cur.y]=1;
	queue<node> qu;
	qu.push(cur);
	while(!qu.empty())
	{
		cur=qu.front();
		qu.pop();
		if(ok(cur.x,cur.y)&&Map[cur.x][cur.y]==num)
		{
			sum+=cur.steps;
			return;
		}
		for(int i=1;i<=4;i++)
		{
			if(i==1)
			{
				nex.x=cur.x+1;
				nex.y=cur.y;
			}
			if(i==2)
			{
				nex.x=cur.x;
				nex.y=cur.y+1;
			}
			if(i==3)
			{
				nex.x=cur.x-1;
				nex.y=nex.y;
			}
			if(i==4)
			{
				nex.x=cur.x;
				nex.y=cur.y-1;
			}
			if(ok(nex.x,nex.y)&&!vis[nex.x][nex.y])
			{
				nex.steps=cur.steps+1;
				vis[nex.x][nex.y]=1;
				qu.push(nex);
			}
		}
	}
}
int main()
{
	sum=0;
	memset(Map,0,sizeof(Map));
	memset(vis,0,sizeof(vis));
	cin>>n>>m;
	for(int i=0; i<n; i++)
	{
		for(int j=0; j<m; j++)
		{
			cin>>Map[i][j];
		}
	}
	for(int i=0; i<n; i++)
	{
		for(int j=0; j<m; j++)
		{
			bfs(Map[i][j],i,j);
		}
	}
	printf("%d",sum);
}

结果当然是毫无意外的挂掉了,因为除去bfs搜索的时间不算,本身求曼哈顿距离之和就是O(n^2)的操作。(不知道这个时间复杂度算的对不对。。。)所以肯定超时了。

所以怎么降低时间复杂度的呢?

后来看一下tutorial,发现可以用排序加类似前缀和的操作将时间复杂度降至O(n),

 

思路就是说,先用一个数据结构存一下每个点的颜色、横纵坐标,然后把点对曼哈顿距离和的贡献算进答案。还有就是横坐标与纵坐标是可以分开算的,操作相同。

#include<cstdio>
#include<algorithm>
#include<set>
#include<vector>

using namespace std;

int x;
set<int> color;
vector<long long> row[100005], col[100005];

int main()
{
	int n, m;
	scanf_s("%d %d", &n, &m);

	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			scanf_s("%d", &x);
			if (color.count(x) == 0)
				color.insert(x);//记录颜色
			row[x].push_back(i);//记录颜色对应横坐标
			col[x].push_back(j);//记录颜色对应纵坐标
		}
	}

	long long ans = 0;

	for (set <int> :: iterator it = color.begin(); it != color.end(); it++)
	{
		sort(row[*it].begin(), row[*it].end());
		for (int j = 0; j < row[*it].size(); j++)
		{
			ans += (long long)1 *row[*it][j] * j -  (long long)1 *row[*it][j] * (row[*it].size() - j - 1);//求前缀和
		}

		sort(col[*it].begin(), col[*it].end());
		for (int j = 0; j < col[*it].size(); j++)
		{
			ans += (long long)1*col[*it][j] * j - (long long)1* col[*it][j] * (col[*it].size() - j - 1);
		}
	}
	printf("%lld\n", ans);
	return 0;
}

那本题的关键就在于求那个序列的前缀和的求和,通过sort可以去绝对值,通过类似前缀和的思想来降低时间复杂度。

(对于本蒟蒻来说,真的是很复杂的一道题,coding之路漫漫,不知道什么时候能上绿名啊)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值