篱笆题目出处(3075)
这道题是我们编程社考试中第三次的第二题,当时都有一些思路,但是时间紧,没打出来
读了题之后,第一思路是暴力,然后是最小生成树,最后是才想到贪心
首先,暴搜肯定会超时
然后,用最小生成树在小数据时加点优化可以勉强卡过
但是贪心才是正解
先来讲一讲最小生成树做法
在m和n都小于2000时可以卡过
大致思路:
以所有篱笆的交点作为节点,以篱笆的长度作为边权
然后就是求一个最小生成树
但是2000*2000=4000000,nlogn的算法会超时
这时,我们便要优化一下
因为在每一列或者每一行的篱笆的长度都是相同的
所以我们只需要从每一段抽出一个样本来排序就可以了
下面是同校机房大佬的一个最小生成树做法:
PS:勉强可以卡过,但数据大了会RE
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 2001
int n,m,a[MAXN+10],b[MAXN+10],fa[MAXN*MAXN+10];
long long ans;
void read()//读入部分
{
int A,B;
scanf("%d%d%d%d",&A,&B,&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
a[n+1]=A-a[n];
++n;
for(int i=n-1;i>=1;i--)
a[i]-=a[i-1];
for(int i=1;i<=m;i++)//计算横着的边权
scanf("%d",&b[i]);
sort(b+1,b+m+1);
b[m+1]=B-b[m];
++m;
for(int i=m-1;i>=1;i--)
b[i]-=b[i-1];//计算竖着的边权
sort(a+1,a+n+1);
sort(b+1,b+m+1);//对边权从小到大排序
}
int find(int x){
if(fa[x]==x) return x;
return find(fa[x]);
}//最小生成树
void workout()
{
for(int i=1;i<=n*m;i++)
fa[i]=i;
for(int i=1,j=1;i<=n||j<=m;){
if(i<=n&&(j>m||a[i]<b[j])){
for(int k=1,num,x,y;k<m;k++){
num=(i-1)*m+k;
x=find(num),y=find(num+1);
if(x!=y){
fa[y]=x;
ans+=1LL*a[i];
}
}
i++;
}
else{
for(int k=1,num,x,y;k<n;k++){
num=(k-1)*m+j;
x=find(num),y=find(num+m);
if(x!=y){
fa[y]=x;
ans+=1LL*b[j];
}
}
j++;
}
}//循环那个加进去会小一些
printf("%lld\n",ans);
}
int main()
{
read();
workout();
return 0;
}
我解释得不是很清楚,这里有比这个更清楚的同校机房大佬的博客
然后就是贪心的做法了
大致思路:
首先,我们需要至少把一列和一行的篱笆全部打通
然后每一次打通篱笆之后我们就要少乘一个
比如
对于这个图,当我们打通一些之后,现在要打蓝色箭头的这一个:
那我们发现我们只用打三个了
那我们就由此可以打出正解::
#include<cstdio>
#include<algorithm>
using namespace std;
inline void read(int &x) {
x=0;
int f=1;
char s=getchar();
while(s<'0'||s>'9') {
if(s=='-')
f=-1;
s=getchar();
}
while(s>='0'&&s<='9') {
x=x*10+s-48;
s=getchar();
}
x*=f;
}
inline void pr(long long x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>9)
pr(x/10);
putchar(x%10+48);
}//快读快输不解释
int A,B,a[2005],b[2005],i,n,j,k,m,x=1,y=1;
long long ans;
int main() {
read(A),read(B),read(n),read(m);
for(i=1;i<=n;i++)
read(a[i]);
for(i=1;i<=m;i++)
read(b[i]);
sort(a+1,a+1+n);
sort(b+1,b+1+m);//先要对每一块篱笆的位置进行排序
a[n+1]=A;
b[m+1]=B;//方便计算
for(i=0;i<=n;i++)
a[i]=a[i+1]-a[i];
for(i=0;i<=m;i++)
b[i]=b[i+1]-b[i];//计算每段篱笆的距离
sort(a,a+1+n);
sort(b,b+1+m);//对距离排序,每次都要求最小的
ans=a[0]*m+b[0]*n;//最短的距离
while(x<=n&&y<=m)//当有一个打通了,其他的就都打通了
if(a[x]<=b[y])//先切断小的
ans+=a[x++]*(m-y+1);
else
ans+=b[y++]*(n-x+1);
pr(ans);
}
大概就是这个样子