今天突然心血来潮,想到之前欠下了很多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之路漫漫,不知道什么时候能上绿名啊)