296. 最佳的碰头地点

Q:

在这里插入图片描述

A:

1.暴力计算,MN个节点,每个节点遍历MN次,O(M^2* N^2)
代码不贴了
2.我写的DP,时间O(MN),空间O(MN)
代码(有点长,但没什么技巧,就是强行保存算过的值)

class Solution {
public:
    int minTotalDistance(vector<vector<int>>& grid) 
    {
        int row=grid.size(),col=grid[0].size();
        vector<int> row_cnt(row,0),col_cnt(col,0);    //每行的人数和、每列的人数和
        vector<int> row_until_cnt(row,0),col_until_cnt(col,0);
        int cnt=0;
        for(int i=0;i<row;++i)
        {
            for(int j=0;j<col;++j)
            {
                row_cnt[i]+=grid[i][j];
                cnt+=grid[i][j];
            }
            if(i>0)
            {
                row_until_cnt[i]=row_until_cnt[i-1]+row_cnt[i-1];
            }
        }
        for(int i=0;i<col;++i)
        {
            for(int j=0;j<row;++j)
            {
                col_cnt[i]+=grid[j][i];
            }
            if(i>0)
            {
                col_until_cnt[i]=col_until_cnt[i-1]+col_cnt[i-1];
            }
        }
        //选择哪一行做碰面行?这一行上面的所有节点到该行的距离和下面的所有节点到该行的距离之和最小
        vector<int> dp_row1(row,0),dp_row2(row,0);
        for(int i=1;i<row;++i)  //截止到第i-1行,想到达第i行的下行距离
        {
            dp_row1[i]=dp_row1[i-1]+row_until_cnt[i];
        }
        for(int i=row-2;i>=0;--i)   //截止到i+1行,想到达第i行的上行距离
        {
            dp_row2[i]=dp_row2[i+1]+(cnt-row_cnt[i]-row_until_cnt[i]);
        }
        int min_row,min_row_sum=INT_MAX;
        for(int i=0;i<row;++i)
        {
            if(dp_row1[i]+dp_row2[i]<min_row_sum)
            {
                min_row_sum=dp_row1[i]+dp_row2[i];
                min_row=i;  //可算找出哪一行最优了 我日 写了五十行
            }
        }
        //下面如法炮制对列做上面同样的操作,从而找出最优行
        vector<int>dp_col1(col,0),dp_col2(col,0);
        for(int i=1;i<col;++i)
        {
            dp_col1[i]=dp_col1[i-1]+col_until_cnt[i];
        }
        for(int i=col-2;i>=0;--i)
        {
            dp_col2[i]=dp_col2[i+1]+(cnt-col_cnt[i]-col_until_cnt[i]);
        }
        int min_col,min_col_sum=INT_MAX;
        for(int i=0;i<col;++i)
        {
            if(dp_col1[i]+dp_col2[i]<min_col_sum)
            {
                min_col_sum=dp_col1[i]+dp_col2[i];
                min_col=i;  //最优列
            }
        }
        cout<<min_row<<" "<<min_col;
        return min_col_sum+min_row_sum;
    }
};

3.评论区看见的代码:
贴个原评论地址
分析:首先作者也是分x、y轴方向分别考虑的。 对于x轴,记录每个人的家所在位置的行号,记录为数组vx。然后直接找vx的中位数作为最终集合点的行号,为什么呢? 简易证明下:一个序列sequence,中位数为mid。令mid做集合点的集合距离和为X。当mid-1做集合点时,所有mid左边点的集合距离都减1,包括mid以及mid右边点的集合距离都加1。你可以注意到,减一的点数比加一的点数少了一个(也就是mid本身)。如果再往左移,总集合距离会更大。往右移也是同样的道理,证毕。
然后另一个问题: 为什么遍历完之后找vx中位数的时候直接找没有排序,而vy进行了排序。这是因为在前面两个for遍历时,是从上至下每一行依次来的,故小的行号肯定在前面,vx本来就是有序序列。
而vy只是局部有序的,两个for遍历时,每一行中列号是递增的,但遍历到下一行就不再是有序的了。故作者在这里要单独对vy做排序处理,而vx不必。
这个算法时间O(MN*log(MN)),vy排序。空间O(max(M,N,long(MN))。

class Solution {
public:
	int minTotalDistance(vector<vector<int>>& grid) {
		vector<int> vx;
		vector<int> vy;
		for (int x = 0; x < grid.size(); x++) {
			for (int y = 0; y < grid[x].size(); y++) {
				if (grid[x][y] > 0) {
					vx.push_back(x);
					vy.push_back(y);
				}
			}
		}
		sort(vy.begin(), vy.end());
		int mx = vx[vx.size() / 2];
		int my = vy[vy.size() / 2];

		int ans = 0;
		for (int x : vx) ans += abs(x - mx);
		for (int y : vy) ans += abs(y - my);
		return ans;
	}
};
posted @ 2019-10-31 18:57  NeoZy  阅读(450)  评论(0编辑  收藏  举报