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;
    }
    
  • 相关阅读:
    TOMCAT源码分析(转)
    CSS从零开始(1)--CSS基础语法
    java生成验证码
    [转]java中作用域public private protected 以及不写的区别
    Java Socket NIO详解(转)
    mysql数据库单表只有一个主键自增id字段,ibatis实现id自增
    手机验证码执行流程
    javaweb之Filter过滤器详解
    HttpSessionListener的用法
    SpringContextHolder 静态持有SpringContext的引用
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998563.html
Copyright © 2011-2022 走看看