• ybt1197;p4677 山区建小学(博客里第一道蓝题)


    ybt1197;p4677 山区建小学

    【题目描述】

    政府在某山区修建了一条道路,恰好穿越总共m个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往。已知任意两个相邻的村庄之间的距离为di(为正整数),其中,0<i<m。为了提高山区的文化素质,政府又决定从m个村中选择n个村建小学(设0<n≤m<500)。请根据给定的m、n以及所有相邻村庄的距离,选择在哪些村庄建小学,才使得所有村到最近小学的距离总和最小,计算最小值。

    【输入】

    第1行为m和n,其间用空格间隔

    第2行为m−1 个整数,依次表示从一端到另一端的相邻村庄的距离,整数之间以空格间隔。

    例如:

    10 3
    2 4 6 5 2 4 3 1 3
    

    表示在10个村庄建3所学校。第1个村庄与第2个村庄距离为2,第2个村庄与第3个村庄距离为4,第3个村庄与第4个村庄距离为6,...,第9个村庄到第10个村庄的距离为3。

    【输出】

    各村庄到最近学校的距离之和的最小值。

    【输入样例】

    10 2
    3 1 3 1 1 1 1 1 3
    

    【输出样例】

    18
    

    ybt递推的最后一道题,这是一道蓝题,像我这种弱菜不看题解肯定是做不出来滴!

    我们在小学的时候就学过,在数轴上有若干个点,取一点使原来的点到该点距离最小的问题

    若点的个数为奇数,那么就取最中间的点;

    若个数为偶数,那么在中间两个点之间以及两点连成的线段也是最短的策略。

    本题要放置多个学校,所以我们要分解问题。

    首先,一个基本推论:每个学校服务的村子应该是连续不断的一个区间。

    证明

    首先,设一个学校A服务的村庄中最靠左的和最靠右的分别为x和y,那么在A的左边,x的右边,所有的村子到A的距离都比x的要小。而且既然x被A服务,说明它到A左边学校的距离比到A的距离远,所以(x,A)区间内所有村子到A的距离都比到A左边的学校距离近。A右边则同理,所以若x,y被A服务,那么[x,y]区间内所有村子都被A服务。而A所在的村子就是第(x+y)/2个村子(上文的小学知识)

    这样一来,问题就变成寻找最合适的方案将m个村子分成n个区间的问题。

    这样就可以用f~i,j~来表示将i个村子分成j个区间的最小距离和。

    f~i,j~的转移可以考虑j从小到大推,j每次加一就相当于在之前基础上新建一所小学,但是由于状态只存储了方案的结果,没有存储学校的具体位置,所以无法决定学校建在哪,所以就考虑在原来的边界i~1~基础上,向右将边界拓展到i~2~,这样(i~1~,i~2~]区间内所有村子都被新建的学校服务。

    这个算法,i~2~就是i,新学校就是第j所,我们只要枚举i~1~,从所有可行的i~1~中选出最优的给f~i,j~赋值即可。

    由于每次转移都要用到第二维为j-1时的数组值,所以j在循环外层。

    另外,因为转移过程中会频繁用到任意区间内建一所学校的路程和,所以建一个数组s来预处理。

    并且为了计算方便,在读入的同时用前缀和的方式存储每个村子的坐标:

    a[1]=0;
    for(int i=2;i<=m;i++) {
    	cin>>a[i];
    	a[i]+=a[i-1];
    	//cout<<i<<"	"<<a[i]<<endl;
    }
    for(int i=1;i<=m;i++) {
    	for(int l=1;l<=m-i;l++) {
    		s[l][l+i]=s[l][l+i-1]+a[l+i]-a[l+(i/2)];//这个递推方程(我也懒得)证明,总之画图找一下规律就出来了
    		//cout<<l<<" "<<i<<" "<<s[l][l+i]<<endl;
    	}
    }
    

    处理好s数组,就可以进行转移了,根据上文的思路,写出方程:

    f~i,j~=min(f~i,j~,f~k,j-1~+s~k,i~)

    接下来上AC代码:

    #include<iostream>
    #include<cstring>
    using namespace std;
    long long f[505][505],a[505],s[505][505],m,n,Ans=0;//邻村距离,m个村,n个学校 
    int main() {
    	cin>>m>>n;
    	a[1]=0;
    	for(int i=2;i<=m;i++) {
    		cin>>a[i];
    		a[i]+=a[i-1];
    		//cout<<i<<"	"<<a[i]<<endl;
    	}
    	for(int i=1;i<=m;i++) {
    		for(int l=1;l<=m-i;l++) {
    			s[l][l+i]=s[l][l+i-1]+a[l+i]-a[l+(i/2)];
    			//cout<<l<<" "<<i<<" "<<s[l][l+i]<<endl;
    		}
    	}
    	memset(f,0x7f,sizeof(f));//因为后面要取最小值,所以给f数组附上较大的初始值,保证f数组在处理过程中被更新。
    	for(int i=1;i<=m;i++){
    		f[i][1]=s[1][i];
    	}
    	for(int j=2;j<=n;j++) {//枚举学校数 
    		for(int i=j;i<=m;i++) {//枚举村子数(不得小于学校数) 
    			for(int k=1;k<i;k++) {//枚举第j所学校的服务区间(前文提到,左开右闭)左边界(不得大于或等于总村子数i) 
    				f[i][j]=min(f[i][j],f[k][j-1]+s[k+1][i]);
    				//cout<<i<<" "<<j<<"	"<<f[i][j]<<endl;
    			} 
    		}
    	}
    	cout<<f[m][n]<<endl;
    	return 0;
    }
    

    感谢题解的启发(只是看了思路,其实文章和推理都是自己写的)

  • 相关阅读:
    C# MQTT M2MQTT
    C# MethodInvoker委托的使用
    linux打包/解压-tar
    linux挂载查看、添加与取消
    IE6、火狐不支持a:visited
    js和jquery中的触发事件
    MySQL5.7.9免安装版配置方法
    mysql数据库在Navicat Premium连接的时候出现1862错误
    mysql max_allowed_packet查询和修改
    服务器是windows时tomcat无法打印所有日志配置修改
  • 原文地址:https://www.cnblogs.com/Wild-Donkey/p/12221297.html
Copyright © 2020-2023  润新知