zoukankan      html  css  js  c++  java
  • 并查集+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
    ")
    const int maxn = 500 + 5;
    int par[maxn * maxn], cnt[maxn * maxn];
    bool vis[maxn * maxn];
    int n, k;
    char ch[maxn][maxn];
    int dx[] = {1, 0, -1, 0};
    int dy[] = {0, 1, 0, -1};
    
    int bfs(int x, int y, int fa){
        queue<pair<int, int> > que;
        que.push(mk(x, y));
        int cnt = 1;
        while (!que.empty()){
            pair<int, int> u = que.front(); que.pop();
            for (int i = 0; i < 4; i++){
                int nx = u.fi + dx[i], ny = u.se + dy[i];
                if (nx <= 0 || ny <= 0 || 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 = 1; x <= n; x++)
            for (int y = 1; y <= n; y++)
                pointcnt[x * n + y] = vis[x * n + y] = 0;
    
        int cntx = 0;
        for (int x = i; x <= k + i - 1; x++)///take care of border
            for (int y = 1; y < k; y++){///最后一列先不放进去
                if (ch[x][y] == '.') pointcnt[par[x * n + y]]++;
                if (ch[x][y] == 'X') cntx++;
            }
        return cntx;
    }
    
    int solve(){
        int ans = 0;
        for (int i = 1; i <= n - k + 1; i++){
            int cntx = init(i);
            for (int y = 1; y <= n - k + 1; y++){///四个方向的遍历
                ///clear上一列的,insert目前列的
                for (int x = i; x <= i + k - 1; x++){
                    if (ch[x][y - 1] == '.')  pointcnt[par[x * n + y - 1]]--;
                    else if (ch[x][y - 1] == 'X') cntx--;
                    if (ch[x][y + k - 1] == '.') pointcnt[par[x * n + y + k - 1]]++;
                    else if (ch[x][y + k - 1] == 'X') cntx++;
                }
                int pin = 0;
                vector<int> v;
                ///左右列, 变行
                for (int x = i; x <= i + k - 1; x++){
                    for (int j = 0; j < 4; j++){
                        int nx = x + dx[j], ny = y + dy[j];
                        if (nx <= 0 || ny <= 0 || 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 = 0; j < 4; j++){
                        int nx = x + dx[j], ny = y + k - 1 + dy[j];
                        if (nx <= 0 || ny <= 0 || 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 - 1; yy++){
                    for (int j = 0; j < 4; j++){
                        int nx = i + dx[j], ny = yy + dy[j];
                        if (nx <= 0 || ny <= 0 || 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 = 0; j < 4; j++){
                        int nx = i + k - 1 + dx[j], ny = yy + dy[j];
                        if (nx <= 0 || ny <= 0 || 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 = 0; 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 = 1; i <= n; i++) scanf("%s", ch[i] + 1);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                par[i * n + j] = i * n + j;
        for (int i = 1; i <= n; i++){
            for (int j = 1; 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
    ", solve());
        return 0;
    }
    View Code

    然后敲完之后跑出来是2600ms,然后有人是300ms左右就过了的,具体思路差不多,大概就是我初始化+vector多了一堆没用的东西导致复杂度变得很高了。

    不过我也看了一下他们的代码发现,果然差距还是好大

    代码来源:http://blog.csdn.net/Fsss_7/article/details/51706546#reply

    #include<bits/stdc++.h>
    using namespace std;
    const int N=510;
    typedef long long ll;
    typedef unsigned long long ull;
    char s[N];
    int n,a[2*N][2*N],b[N][N];
    int g=0,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>1&&b[x-1][y]==-1) dfs(x-1,y);
        if (x<n&&b[x+1][y]==-1) dfs(x+1,y);
        if (y>1&&b[x][y-1]==-1) dfs(x,y-1);
        if (y<n&&b[x][y+1]==-1) dfs(x,y+1);
    
    }
    
    int main(){
        int i,j,h,k,tot,ans=0;
        scanf("%d%d", &n, &k);
        for (i=1;i<=n;i++) {
            scanf("%s", s);
            for (j=1;j<=n;j++)
            if (s[j-1]=='.') b[i][j]=-1;///存在点
            else {
                b[i][j]=0;///不是点
                a[i][j]+=1;a[i][j+k]+=-1;///然后标记障碍物所对应的方框
                a[i+k][j]+=-1;a[i+k][j+k]+=1;
            }
        }
        memset(xm,0x7f,sizeof(xm));
        memset(ym,0x7f,sizeof(ym));
        for (i=1;i<=n;i++)
            for (j=1;j<=n;j++)
                if (b[i][j]==-1) {
                    g++,dfs(i,j);
                    if (xx[g]-xm[g]+1<=k&&yx[g]-ym[g]+1<=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 = 1; i <= n; i++){
            for (int j = 1; j <= n; j++){
                printf("%d ", a[i][j]);
            }
            cout << endl;
        }
    
        for (i=1;i<=n;i++)///a[i][j]是统计(i,j)~(i-k, j-k)这个矩形中,只在内部的‘点’和X的数目
            for (j=1;j<=n;j++) 
                a[i][j]+= a[i][j-1] + a[i-1][j] - a[i-1][j-1];
        for (i=k;i<=n;i++)
            for (j=k;j<=n;j++) {///枚举(k,k)开始的端点
                tot=0;
                for (h=j-k+1;h<=j;h++)///左列
                    if (!q[b[i-k][h]]) q[b[i-k][h]]=1,tot+=f[b[i-k][h]];///用q标记该连通块是否被访问过,然后b所记录的是第几个块的标号
                for (h=j-k+1;h<=j;h++)///右列
                    if (!q[b[i+1][h]]) q[b[i+1][h]]=1,tot+=f[b[i+1][h]];
                for (h=i-k+1;h<=i;h++)///上行
                    if (!q[b[h][j-k]]) q[b[h][j-k]]=1,tot+=f[b[h][j-k]];
                for (h=i-k+1;h<=i;h++)///下行
                    if (!q[b[h][j+1]]) q[b[h][j+1]]=1,tot+=f[b[h][j+1]];
                ans=max(ans,tot+a[i][j]);
                printf("tot = %d a[%d][%d] = %d
    ", tot, i, j, a[i][j]);
                ///printf("a[%d][%d] = %d
    ", i, j, a[i][j]);
                ///初始化
                for (h=j-k+1;h<=j;h++) q[b[i-k][h]]=0;
                for (h=j-k+1;h<=j;h++) q[b[i+1][h]]=0;
                for (h=i-k+1;h<=i;h++) q[b[h][j-k]]=0;
                for (h=i-k+1;h<=i;h++) q[b[h][j+1]]=0;
            }
        printf("%d
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    linq的多表查询
    markdown语法
    遍历Hashtable、IDictionary、Dictionary<string, string>
    DOS修改文件夹权限
    kangle 3.2.0 发布,国产开源web服务器
    nat上传文件到google
    黄聪:C#中用ILMerge将所有引用的DLL和exe文件打成一个exe文件,有图解
    UltiDev Web Server Pro
    vs2010 命令行下用 msbuild 发布web站点
    asp.net重启网站
  • 原文地址:https://www.cnblogs.com/heimao5027/p/5925844.html
Copyright © 2011-2022 走看看