Tyvj P1673 位图 题解 O(n^2)

原题地址

分析:

朴素的想法是从已知的白点往外扩展,就像自己拿笔在图上标数一样,肯定是从最好看到的标起。看到Tyvj上有位仁兄的题解就是这么写的,他的建议是用队列保存已被标记且等待用其标记其它点的“候选点”,依次取出队首的点,用其进行标记,并将新标记的点放到队尾。但应在放入时判重,因为一个点可能被不同的候选点标记多次并多次放到队尾,就会造成重复。如果不做优化(像我这种SX),就会TLE最后两个点。后来没有想到什么好的优化方法,就改用了下面的方法。

对于一个点O,它到棋盘上某一白点的连线,只能分为四种,即左上、左下、右上、右下,不可能先往左走,又拐向右边,这样一定不是最短距离。因此对于每个待标记的黑点,它的ans为其到其左上部分最近白点的距离…到其右下部分最近白点的距离中的最小值。于是把整个问题拆成四部分,即每个点的左上ans、左下ans、右上ans、右下ans,而每一部分都是很好求的。以左上部分为例,点(x,y)只需关注其左边的点和上边的点的标记即可,即ans(x,y)=min( ans(x-1,y), ans(x,y-1) )+1 ,这个转移方程还是蛮好想的。当然,白点的标记初始时就是0喽,在读入时就可以标记上了。最后拿四个ans取个min就可以了。算法复杂度应该是O(n^2),反正是不会爆时间了。

我的渣渣代码
(人笨不会打表,就是手累点呗,还好反正不用转脑子)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main(){
    int m,n;
    cin>>m>>n;
    int ans[m+5][n+5],dp[4][m+5][n+5];
    memset(ans,0x3f,sizeof(ans));
    memset(dp,0x3f,sizeof(dp));
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            char c;
            cin>>c;
            if(c=='1'){
                ans[i][j]=0;
                for(int k=0;k<4;k++)
                    dp[k][i][j]=0;
            }
        }
    int k=0;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            if(i>1)
                dp[k][i][j]=min(dp[k][i][j],dp[k][i-1][j]+1);
            if(j>1)
                dp[k][i][j]=min(dp[k][i][j],dp[k][i][j-1]+1);
        }
    k=1;
    for(int i=1;i<=m;i++)
        for(int j=n;j>0;j--){
            if(i>1)
                dp[k][i][j]=min(dp[k][i][j],dp[k][i-1][j]+1);
            if(j<n)
                dp[k][i][j]=min(dp[k][i][j],dp[k][i][j+1]+1);
        }
    k=2;
    for(int i=m;i>0;i--)
        for(int j=n;j>0;j--){
            if(i<m)
                dp[k][i][j]=min(dp[k][i][j],dp[k][i+1][j]+1);
            if(j<n)
                dp[k][i][j]=min(dp[k][i][j],dp[k][i][j+1]+1);
        }
    k=3;
    for(int i=m;i>0;i--)


        for(int j=1;j<=n;j++){
            if(i<m)
                dp[k][i][j]=min(dp[k][i][j],dp[k][i+1][j]+1);
            if(j>1)
                dp[k][i][j]=min(dp[k][i][j],dp[k][i][j-1]+1);
        }
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            for(k=0;k<4;k++)
                ans[i][j]=min(ans[i][j],dp[k][i][j]);
    for(int i=1;i<=m;i++,cout<<endl)
        for(int j=1;j<=n;j++)
            cout<<ans[i][j]<<" ";
    return 0;
}

100%的测试数据 time<=100ms

By YOUSIKI

第一次在Blog里写题解,留个小尾巴纪念一下

你可能感兴趣的:(Tyvj P1673 位图 题解 O(n^2))