zoukankan      html  css  js  c++  java
  • 【JZOJ5775】农夫约的假期【模拟】

    题目大意:

    题目链接:https://jzoj.net/senior/#main/show/5775
    题目图片:
    https://www.z4a.net/images/2018/09/23/1.png
    https://www.z4a.net/images/2018/09/23/2.md.png
    https://www.z4a.net/images/2018/09/23/3.png
    https://www.z4a.net/images/2018/09/23/4.png
    n×nn\times n的矩形中找出一个点,使得这个点到其他标记点曼哈顿距离加上标记点的权值之和最小。


    思路:

    1010分做法:

    n10n\leq 10
    枚举这个矩阵的每一个点,再枚举每一个标记点,求出曼哈顿距离,取最小值即可。
    或者从每个标记点跑BFSBFS
    时间复杂度:O(n4)O(n^4)
    10分代码


    30分做法:

    n1000n\leq 1000
    假设这个图是这样子的(黄色点是标记点):

    在这里插入图片描述

    可以把第三行的所有点和第一行的特殊点的曼哈顿距离连一下看看。
    在这里插入图片描述

    会发现,它们向下的距离(蓝色部分)是一样的!
    在这里插入图片描述

    同理,下面的标记点也一样。
    在这里插入图片描述
    所以,任何一个特殊点到同一行的点的曼哈顿距离中经过的列数是一样的。
    同理,所有的特殊点到同一列的点的曼哈顿距离中经过的行数是一样的。
    那么我们设h[i]h[i]表示所有特殊点到这一行的点曼哈顿距离经过的列数,l[i]l[i]表示所有特殊点到这一列的点曼哈顿距离经过的行数。
    那么我们就先初始化出每行每列的特殊点的个数,然后O(n2)O(n^2)枚举任意两行,求出第jj行的特殊点到第ii行的所有点的曼哈顿距离的列数(同理也要枚举任意两列)
    最后可以O(n2)O(n^2)枚举所有点,蔬输出最小的那一个。
    30分代码


    100100分做法:

    O(n)O(n)
    考虑如何在30分的做法上优化。跳过了30分的朋友建议回去再可以下30分的做法。
    我们可以发现,第ii行的点到所有特殊点的曼哈顿距离之和是可以通过第i1i-1行的点得到的。如果能O(1)O(1)转换,那么就可以了。
    首先,很容易发现:
    h[i]=h[i1]+i1ih[i]=h[i-1]+在第i-1行上面的特殊点数量-在第i行下面的特殊点数量
    那么如果能O(1)O(1)知道在任意一行上面的点的数量和下面的点的数量就可以O(1)O(1)转化。
    所以可以求一个前缀和,hs[i]hs[i]表示在第ii行(含)之前的特殊点数量,那么就有
    hs[i]=hs[i1]+hnum[i]hs[i]=hs[i-1]+hnum[i]
    知道了上面的特殊点数量,那么下面的特殊点数量就一目了然了。
    那么就可以先O(n)O(n)求出第一行的点到所有特殊点的曼哈顿距离,然后就可以推出每一行到特殊点的距离。
    列也同理,可以O(n)O(n)求出。
    那么我们就做到了:

    • O(n)O(n)求出每一行的点到所有特殊点的列的曼哈顿距离之和
    • O(n)O(n)求出每一列的点到所有特殊点的行的曼哈顿距离之和

    那么就只剩下最后一个O(n2)O(n^2)的地方了:输出。
    我们可以O(n)O(n)知道所有行的最小列曼哈顿距离,又可以O(n)O(n)知道所有列的最小曼哈顿距离,那么这两行的交点就是到所有标记点曼哈顿距离最短的点!
    在这里插入图片描述


    代码:

    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #define ll long long
    #define N 110000
    using namespace std;
    
    ll n,m,x,y,q,z,h[N],l[N],hnum[N],lnum[N],hs[N],ls[N],minn,sum;
    
    int main()
    {
    	scanf("%lld%lld%lld",&n,&m,&z);
    	for (ll i=1;i<=m;i++)
    	{
    		scanf("%lld%lld%lld",&x,&y,&q);
    		hnum[x]++;
    		lnum[y]++;  //每行每列的标记点数量
    		sum+=q;
    	}
    	for (ll i=1;i<=n;i++)
    	 hs[i]=hs[i-1]+hnum[i];
    	for (ll i=1;i<=n;i++)
    	 ls[i]=ls[i-1]+lnum[i];  //前缀和
    	for (ll i=1;i<=n;i++)
    	 h[1]+=(hnum[i]*abs(i-1)*z);
    	for (ll i=1;i<=n;i++)
    	 l[1]+=(lnum[i]*abs(i-1)*z);  //求出第一行第一列
    	for (ll i=2;i<=n;i++)
    	 h[i]=h[i-1]+hs[i-1]*z-(m-hs[i-1])*z;
    	for (ll i=2;i<=n;i++)
    	 l[i]=l[i-1]+ls[i-1]*z-(m-ls[i-1])*z;  //往后转移
    	minn=1e16;
    	for (ll i=1;i<=n;i++)
    	 if (h[i]<minn)   //找最小的行
    	 {
    	 	minn=h[i];
    	 	x=i;
    	 }
    	minn=1e16;
    	for (ll i=1;i<=n;i++)
    	 if (l[i]<minn)  //找最小的列
    	 {
    	 	minn=l[i];
    	 	y=i;
    	 }
    	cout<<h[x]+l[y]+sum<<"\n"<<x<<" "<<y;
    	return 0;
    }
    
  • 相关阅读:
    670. Maximum Swap
    653. Two Sum IV
    639. Decode Ways II
    636. Exclusive Time of Functions
    621. Task Scheduler
    572. Subtree of Another Tree
    554. Brick Wall
    543. Diameter of Binary Tree
    535. Encode and Decode TinyURL
    博客园自定义背景图片
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998562.html
Copyright © 2011-2022 走看看