题目描述
在给定的图中有N*M个k值,位置为(x,y),现求出一个位置(x1,x2),使得最小;
∑ni=1∑nj=1Kij∗((x−xi)2+(y−yj)2)
30分打法
暴力枚举,可以不用枚举边界(这样一定不是最优),只需要枚举图的一半;
AC做法
对于所求的式子可以将x,y拆分出来,先预处理出每一行与每一列的kij的和然后分别枚举x,y,求出最小的sum{x},最小的sum{y},答案即为sum{x}+sum{y};
∑i=1n∑j=1mKij∗((x−xi)2+(y−yj)2)=∑i=1n(x−xi)2∗∑j=1mKij+∑j=1m(y−yi)2∗∑i=1mKij
代码
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long LL;
int car[1000+10][1000+10];
LL carx[1000+10],cary[1000+10];
int n,m;
LL cnt1=999999999999999999,cnt2=99999999999999999;
LL js1(int i,int x)
{
return LL(carx[i]*(pow(x-4*i+2,2)));//枚举x的位置
}
LL js2(int j,int y)
{
return LL(cary[j]*(pow(y-4*j+2,2)));//枚举y的位置
}
void tot1(int x)
{
LL ans=0;
for(int i=n;i>=0;i--)
{
ans+=js1(i,x);
if(ans>=cnt1) return;//一个小剪枝
}
cnt1=ans;
}
void tot2(int y)
{
LL ans=0;
for(int j=m;j>=0;j--)
{
ans+=js2(j,y);
if(ans>=cnt2) return;
}
cnt2=ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&car[i][j]);
carx[i]+=car[i][j];
cary[j]+=car[i][j];
}
for(int i=4;i<=n*4;i+=4)
{
tot1(i);//枚举
}
for(int j=4;j<=m*4;j+=4)
{
tot2(j);//枚举
}
printf("%lld",cnt1+cnt2);
return 0;
}