http://www.icpc.moe/onlinejudge/showProblem.do?problemCode=3781
题意:
在n*m矩阵的图定义连通区域为x值或y值相同且颜色相同的连通,连通具有传递性
每次可以把一个连通区域颜色反转(O变X,X变O)
问把所有块的颜色变为X最小的步数
思路:
乍一看,翻开随便一个点会影响一大片联通块翻转,实际上只是吓人的,我们其实可以把每个联通块看成一个点。
把原图处理一下,所有在一个联通块的点直接看成一个点,相邻的不同联通块显然颜色不同(否则可以可并为一个块嘛!) 那么我们直接暴力枚举,以任一个联通块作为起点,显然的话,每翻一个联通块,就会把周围所有的联通块并入,形成一个大的联通块 。。直接模拟 就好
初始化联通块并编号,例如从联通块i开始,每一次,把当前联通块周围的所有联通块编号标记为1,然后丢进队列,每次再从队列取出联通块编号,把其周围联通块合并。。。。bfs乱搞就ac了
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
const double pi=acos(-1.0);
double eps=0.000001;
int min(int a,int b)
{return a<b?a:b;}
int max(int a,int b)
{return a>b?a:b;}
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
char mp[45][45];
int id=1;
int vis[45][45];
int flag;
int check[45][45];
int n,m;
struct node
{
int x,y,step;
node(int a=0,int b=0,int c=0){x=a,y=b;step=c;}
};
vector<node > node_in_col[40*40+5];
vector <int> sb[40*40+5];
void dfs(int x,int y )
{
int i,j;
if (vis[x][y]) return ;
flag=1;
vis[x][y]=id;
node_in_col[id].push_back(node(x,y));
for (i=0;i<4;i++)
{
int xx=x+dx[i];
int yy=y+dy[i];
if (!(xx>=1&&xx<=n&&yy>=1&&yy<=m))continue;
if (mp[xx][yy]!=mp[x][y]) continue;
dfs(xx,yy);
}
}
int col[40*40+5];
struct bf
{
int t,c;
bf(int a,int b){c=a;t=b;}
};
int solve(int cc)
{
memset(col,0,sizeof(col));
int ret=0;
queue<bf>q;
int i,j;
bf st(cc,0);
q.push(st);
col[cc]=1;
while(!q.empty())
{
bf tp=q.front();q.pop();
ret=max(ret,tp.t);
int tmp=tp.c;
for (int i=0;i<sb[tmp].size();i++)
{
int tt=sb[tmp][i];
if (col[tt]) continue;
q.push(bf(tt,tp.t+1) );
col[tt]=1;
}
}
return ret;
}
int main()
{
int t;cin>>t;
while(t--)
{
memset(vis,0,sizeof vis);
cin>>n>>m;
int i,j;
for (i=1;i<=n;i++)
scanf("%s",mp[i]+1);
for (i=1;i<=n*m;i++)
sb[i].clear(),node_in_col[i].clear();
id=1;
for (i=1;i<=n;i++)
{
for (j=1;j<=m;j++)
{
flag=0;
dfs(i,j );
if (flag) id++;
}
}
/*for (i=1;i<=n;i++)
{
for (j=1;j<=m;j++)
{
printf("%2d",vis[i][j]);
}
printf("\n");
}*/
for (i=1;i<id;i++)
{
for (j=0;j<node_in_col[i].size();j++)
{
int x=node_in_col[i][j].x;
int y=node_in_col[i][j].y;
for (int k=0;k<4;k++)
{
int xx=x+dx[k];
int yy=y+dy[k];
if( !(xx>=1&&xx<=n&&yy>=1&&yy<=m))continue;
sb[i].push_back(vis[xx][yy]);
}
}
}
/* printf("%d\n",node_in_col[4].size());
for (j=0;j<sb[4].size();j++)
printf("%d ",sb[4][j]);
*/
int minn=40*40+1;
for (i=1;i<id;i++)
minn=min(minn,solve(i));
printf("%d\n",minn);
}
return 0;
}

这是一篇关于ZOJ 3781题目的解题报告。题目涉及一个n*m矩阵的图,其中连通区域由相同x值、y值和颜色定义。通过翻转操作改变颜色,目标是最小化翻转次数使所有区域变为颜色X。解决方法是将每个连通区域视为一个点,采用缩点处理和BFS模拟,从任意连通块开始,每次翻转影响周围联通块,并进行合并,直至所有块颜色变为X。
4396

被折叠的 条评论
为什么被折叠?



