并查集+bfs+暴力滑窗 Codeforces Round #356 (Div. 2) E
http://codeforces.com/contest/680/problem/E
题目大意:给你一个n*n的图,然后图上的 . (我们下面都叫做‘点’)表示可以走,X表示不能走,你有如下的操作,每次你可以选择一个k*k的框,把其中的所有的X都变成 ‘点’,问在该操作后点相连的数目最多是多少?
没看别人代码和思路自己思考并优化了好久,于是敲了两个多小时。。。还是代码能力太差了
思路:当然最最单纯的做法就是O(n^4)。为了优化,起初想用二维树状数组+并查集的,结果发现窗口中的‘点‘’容易造成重复计算,于是换了种方法,每次事先统计‘点’和X,然后再每次暴力枚举边就好了。复杂度O(n*n*4*4*k*log)
//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = + ;
int par[maxn * maxn], cnt[maxn * maxn];
bool vis[maxn * maxn];
int n, k;
char ch[maxn][maxn];
int dx[] = {, , -, };
int dy[] = {, , , -}; int bfs(int x, int y, int fa){
queue<pair<int, int> > que;
que.push(mk(x, y));
int cnt = ;
while (!que.empty()){
pair<int, int> u = que.front(); que.pop();
for (int i = ; i < ; i++){
int nx = u.fi + dx[i], ny = u.se + dy[i];
if (nx <= || ny <= || nx > n || ny > n) continue;
if (ch[nx][ny] != '.' || vis[nx * n + ny]) continue;
vis[nx * n + ny] = true;
cnt++;
par[nx * n + ny] = fa;
que.push(mk(nx, ny));
}
}
return cnt;
} int pointcnt[maxn * maxn];
inline int init(int i){
for (int x = ; x <= n; x++)
for (int y = ; y <= n; y++)
pointcnt[x * n + y] = vis[x * n + y] = ; int cntx = ;
for (int x = i; x <= k + i - ; x++)///take care of border
for (int y = ; y < k; y++){///最后一列先不放进去
if (ch[x][y] == '.') pointcnt[par[x * n + y]]++;
if (ch[x][y] == 'X') cntx++;
}
return cntx;
} int solve(){
int ans = ;
for (int i = ; i <= n - k + ; i++){
int cntx = init(i);
for (int y = ; y <= n - k + ; y++){///四个方向的遍历
///clear上一列的,insert目前列的
for (int x = i; x <= i + k - ; x++){
if (ch[x][y - ] == '.') pointcnt[par[x * n + y - ]]--;
else if (ch[x][y - ] == 'X') cntx--;
if (ch[x][y + k - ] == '.') pointcnt[par[x * n + y + k - ]]++;
else if (ch[x][y + k - ] == 'X') cntx++;
}
int pin = ;
vector<int> v;
///左右列, 变行
for (int x = i; x <= i + k - ; x++){
for (int j = ; j < ; j++){
int nx = x + dx[j], ny = y + dy[j];
if (nx <= || ny <= || nx > n || ny > n) continue;
if (ch[nx][ny] != '.') continue;
int pos = par[nx * n + ny];
if (vis[pos]) continue;
vis[pos] = true; v.push_back(pos); pin += pointcnt[pos];
}
for (int j = ; j < ; j++){
int nx = x + dx[j], ny = y + k - + dy[j];
if (nx <= || ny <= || nx > n || ny > n) continue;
if (ch[nx][ny] != '.') continue;
int pos = par[nx * n + ny];
if (vis[pos]) continue;
vis[pos] = true; v.push_back(pos); pin += pointcnt[pos];
}
}
///上下行,变列
for (int yy = y; yy <= y + k - ; yy++){
for (int j = ; j < ; j++){
int nx = i + dx[j], ny = yy + dy[j];
if (nx <= || ny <= || nx > n || ny > n) continue;
if (ch[nx][ny] != '.') continue;
int pos = par[nx * n + ny];
if (vis[pos]) continue;
vis[pos] = true; v.push_back(pos); pin += pointcnt[pos];
}
for (int j = ; j < ; j++){
int nx = i + k - + dx[j], ny = yy + dy[j];
if (nx <= || ny <= || nx > n || ny > n) continue;
if (ch[nx][ny] != '.') continue;
int pos = par[nx * n + ny];
if (vis[pos]) continue;
vis[pos] = true; v.push_back(pos); pin += pointcnt[pos];
}
}
int len = v.size();
int tmp = k * k - pin;
for (int i = ; i < len; i++){
tmp += cnt[v[i]];
vis[v[i]] = false;
}
ans = max(ans, tmp);
}
}
return ans;
} int main(){
scanf("%d%d", &n, &k);
for (int i = ; i <= n; i++) scanf("%s", ch[i] + );
for (int i = ; i <= n; i++)
for (int j = ; j <= n; j++)
par[i * n + j] = i * n + j;
for (int i = ; i <= n; i++){
for (int j = ; j <= n; j++){
if (ch[i][j] == '.' && !vis[i * n + j]){
vis[i * n + j] = true;
cnt[i * n + j] = bfs(i, j, i * n + j);
}
}
}
printf("%d\n", solve());
return ;
}
然后敲完之后跑出来是2600ms,然后有人是300ms左右就过了的,具体思路差不多,大概就是我初始化+vector多了一堆没用的东西导致复杂度变得很高了。
不过我也看了一下他们的代码发现,果然差距还是好大
代码来源:http://blog.csdn.net/Fsss_7/article/details/51706546#reply
#include<bits/stdc++.h>
using namespace std;
const int N=;
typedef long long ll;
typedef unsigned long long ull;
char s[N];
int n,a[*N][*N],b[N][N];
int g=,q[N*N],f[N*N],xm[N*N],ym[N*N],xx[N*N],yx[N*N]; ///这里应该就是单纯的跑一下,然后知道目前联通块的边界
void dfs(int x,int y) {
b[x][y]=g;f[g]++;///标记是第几个联通快,和记录着联通块的数目
xm[g]=min(xm[g],x);xx[g]=max(xx[g],x);
ym[g]=min(ym[g],y);yx[g]=max(yx[g],y);
if (x>&&b[x-][y]==-) dfs(x-,y);
if (x<n&&b[x+][y]==-) dfs(x+,y);
if (y>&&b[x][y-]==-) dfs(x,y-);
if (y<n&&b[x][y+]==-) dfs(x,y+); } int main(){
int i,j,h,k,tot,ans=;
scanf("%d%d", &n, &k);
for (i=;i<=n;i++) {
scanf("%s", s);
for (j=;j<=n;j++)
if (s[j-]=='.') b[i][j]=-;///存在点
else {
b[i][j]=;///不是点
a[i][j]+=;a[i][j+k]+=-;///然后标记障碍物所对应的方框
a[i+k][j]+=-;a[i+k][j+k]+=;
}
}
memset(xm,0x7f,sizeof(xm));
memset(ym,0x7f,sizeof(ym));
for (i=;i<=n;i++)
for (j=;j<=n;j++)
if (b[i][j]==-) {
g++,dfs(i,j);
if (xx[g]-xm[g]+<=k&&yx[g]-ym[g]+<=k) {///边界超过K的就不会被k*k包围啦,然后对边界进行处理
a[xx[g]][yx[g]]+=f[g]; a[xm[g]+k][ym[g]+k]+=f[g];//左上右下
a[xx[g]][ym[g]+k]-=f[g]; a[xm[g]+k][yx[g]]-=f[g];//左下右上
}
} for (int i = ; i <= n; i++){
for (int j = ; j <= n; j++){
printf("%d ", a[i][j]);
}
cout << endl;
} for (i=;i<=n;i++)///a[i][j]是统计(i,j)~(i-k, j-k)这个矩形中,只在内部的‘点’和X的数目
for (j=;j<=n;j++)
a[i][j]+= a[i][j-] + a[i-][j] - a[i-][j-];
for (i=k;i<=n;i++)
for (j=k;j<=n;j++) {///枚举(k,k)开始的端点
tot=;
for (h=j-k+;h<=j;h++)///左列
if (!q[b[i-k][h]]) q[b[i-k][h]]=,tot+=f[b[i-k][h]];///用q标记该连通块是否被访问过,然后b所记录的是第几个块的标号
for (h=j-k+;h<=j;h++)///右列
if (!q[b[i+][h]]) q[b[i+][h]]=,tot+=f[b[i+][h]];
for (h=i-k+;h<=i;h++)///上行
if (!q[b[h][j-k]]) q[b[h][j-k]]=,tot+=f[b[h][j-k]];
for (h=i-k+;h<=i;h++)///下行
if (!q[b[h][j+]]) q[b[h][j+]]=,tot+=f[b[h][j+]];
ans=max(ans,tot+a[i][j]);
printf("tot = %d a[%d][%d] = %d\n", tot, i, j, a[i][j]);
///printf("a[%d][%d] = %d\n", i, j, a[i][j]);
///初始化
for (h=j-k+;h<=j;h++) q[b[i-k][h]]=;
for (h=j-k+;h<=j;h++) q[b[i+][h]]=;
for (h=i-k+;h<=i;h++) q[b[h][j-k]]=;
for (h=i-k+;h<=i;h++) q[b[h][j+]]=;
}
printf("%d\n", ans);
return ;
}
并查集+bfs+暴力滑窗 Codeforces Round #356 (Div. 2) E的更多相关文章
- Codeforces Round #356 (Div. 1) D. Bear and Chase 暴力
D. Bear and Chase 题目连接: http://codeforces.com/contest/679/problem/D Description Bearland has n citie ...
- Codeforces Round #356 (Div. 2)-A
A. Bear and Five Cards 题目链接:http://codeforces.com/contest/680/problem/A A little bear Limak plays a ...
- Codeforces Round #356 (Div. 2)-B
B. Bear and Finding Criminals 链接:http://codeforces.com/contest/680/problem/B There are n cities in B ...
- Codeforces Round #356 (Div. 2) C. Bear and Prime 100(转)
C. Bear and Prime 100 time limit per test 1 second memory limit per test 256 megabytes input standar ...
- Codeforces Round #356 (Div. 2)B. Bear and Finding Criminals(水题)
B. Bear and Finding Criminals time limit per test 2 seconds memory limit per test 256 megabytes inpu ...
- Codeforces Round #356 (Div. 2)A. Bear and Five Cards(简单模拟)
A. Bear and Five Cards time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- dfs Codeforces Round #356 (Div. 2) D
http://codeforces.com/contest/680/problem/D 题目大意:给你一个大小为X的空间(X<=m),在该空间内,我们要尽量的放一个体积为a*a*a的立方体,且每 ...
- Codeforces Round #356 (Div. 1) C. Bear and Square Grid
C. Bear and Square Grid time limit per test 3 seconds memory limit per test 256 megabytes input stan ...
- Codeforces Round #356 (Div. 2) E. Bear and Square Grid 滑块
E. Bear and Square Grid 题目连接: http://www.codeforces.com/contest/680/problem/E Description You have a ...
随机推荐
- 浅谈position: absolute和position:relative
一.在此先说一下文档流的概念: 1,文档流定义: 百度百科定义:文档流是文档中可显示对象在排列时所占用的位置. 大多网友的理解:元素的位置由元素在 (X)HTML 中的位置决定.将窗体自上而下分成一行 ...
- Python Data Visualization Cookbook 2.2.2
import csv filename = 'ch02-data.csv' data = [] try: with open(filename) as f://用with语句将数据文件绑定到对象f r ...
- crontab格式使用方式
第1列分钟1-59第2列小时1-23(0表示子夜)第3列日1-31第4列月1-12第5列星期0-6(0表示星期天)第6列要运行的命令 下面是crontab的格式:分 时 日 月 星期 要运行的命令 这 ...
- linux jdk环境变量
export JAVA_HOME=/usr/share/jdk8 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/ ...
- [妙味JS基础]第十二课:数组随机、数组去重
知识点总结 json var json={'name':'abc'} 属性加分号为安全的写法 json: 只能用for in 数组:for和for in 都可以使用 json没有length和下标 数 ...
- 【转发】彻底理解 JS 中 this 的指向
为什么要学习this?如果你学过函数式编程,面向对象编程,那你肯定知道干什么用的,如果你没有学过,那么暂时可以不用看这篇文章,当然如果你有兴趣也可以看看,毕竟这是js中必须要掌握的东西. 例子1: f ...
- C# Monads的实现(二)
再谈continuation monad 上一篇中我们已经介绍了continuation monad,但是这个monad与Identity,Maybe,IEnumerable monads稍微难于理解 ...
- inode和文件描述符区别
inode 或i节点是指对文件的索引.如一个系统,所有文件是放在磁盘或flash上,就要编个目录来说明每个文件在什么地方,有什么属性,及大小等.就像书本的目录一样,便于查找和管理.这目录是操作系统需要 ...
- PDO对象
<?php //造DSN:驱动名:dbname=数据库名;host=服务器地址 $dsn = "mysql:dbname=mydb;host=localhost"; //造P ...
- 如何成为出色的IT项目经理:成功的五个关键因素
“出色”的IT 项目经理的定义不是一成不变的.随着经济和商业因素的改变,项目经理的角色进行调整以适应新的需求,迎接新的挑战. 除了一般的困惑之外,还有一种看法就是,在组织中,不同的人对于项目经理的看法 ...