牛客小白赛22 G 仓库选址(容斥/前缀和

这题要求n*m矩阵中选择一个点,使得其他点到这个点的哈密顿距离之和最小

题解用的是O(n4)
我也是很无语

这里可以用O(2)来做

做出无权值的二维前缀和,再分别作出以x和y坐标为权值的二维前缀和

    for (i=1;i<=n;i++){
    	for (j=1;j<=m;j++){
    	scanf ("%d",&a[i][j]);
    	s[i][j]=a[i][j];
   	    sx[i][j]=a[i][j]*i;
  	    sy[i][j]=a[i][j]*j;
  	    s[i][j]+=(s[i][j-1]+s[i-1][j]-s[i-1][j-1]);
 	    sx[i][j]+=(sx[i][j-1]+sx[i-1][j]-sx[i-1][j-1]);
  	    sy[i][j]+=(sy[i][j-1]+sy[i-1][j]-sy[i-1][j-1]);
        }
    }

然后对于题意,枚举每个点为仓库,取出
(无权置的前缀和*x坐标-带x权值的前缀和)+(无权置的前缀和*y坐标-带y权值的前缀和)
这就是广义的答案

对于具体的答案

我们必须分为4大块

在这里插入图片描述

具体的4部分你们看代码吧

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define pi pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define mod 1000000007
using namespace std;
int a[105][105],n,m;
int s[105][105],sx[105][105],sy[105][105];
inline int gs(int a,int b,int c,int d)
{if (a>c||b>d) return 0;
return s[c][d]-s[a-1][d]-s[c][b-1]+s[a-1][b-1];
}
inline int gx(int a,int b,int c,int d)
{if (a>c||b>d) return 0;
return sx[c][d]-sx[a-1][d]-sx[c][b-1]+sx[a-1][b-1];
}
inline int gy(int a,int b,int c,int d)
{if (a>c||b>d) return 0;
return sy[c][d]-sy[a-1][d]-sy[c][b-1]+sy[a-1][b-1];
}
int main (){
    int i,j,k,T;
    scanf ("%d",&T);
    while (T--)
    {scanf ("%d%d",&n,&m);
    swap(n,m);
    memset(s,0,sizeof(s));
    memset(sx,0,sizeof(sx));
    memset(sy,0,sizeof(sy));
    for (i=1;i<=n;i++)
    {for (j=1;j<=m;j++)
    {scanf ("%d",&a[i][j]);
    s[i][j]=a[i][j];
    sx[i][j]=a[i][j]*i;
    sy[i][j]=a[i][j]*j;
    s[i][j]+=(s[i][j-1]+s[i-1][j]-s[i-1][j-1]);
    sx[i][j]+=(sx[i][j-1]+sx[i-1][j]-sx[i-1][j-1]);
    sy[i][j]+=(sy[i][j-1]+sy[i-1][j]-sy[i-1][j-1]);
    }
    }
 /*   cout<<endl;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=m;j++){
    		cout<<s[i][j]<<" ";
		}
		cout<<endl;
	} 
	cout<<endl;
	for(int i=1;i<=n;i++){
    	for(int j=1;j<=m;j++){
    		cout<<sx[i][j]<<" ";
		}
		cout<<endl;
	} 
	cout<<endl;
	for(int i=1;i<=n;i++){
    	for(int j=1;j<=m;j++){
    		cout<<sy[i][j]<<" ";
		}
		cout<<endl;
	} 
	cout<<endl;*/
	
//	cout<<gs(1,1,2,2);
    int ans=200*100*100*1000;
    for (i=1;i<=n;i++)
    {for (j=1;j<=m;j++)
    {int p1=(s[i][j]*i-sx[i][j])+(s[i][j]*j-sy[i][j]);
    int p2=(gs(1,j+1,i,m)*i-gx(1,j+1,i,m))+(gy(1,j+1,i,m)-gs(1,j+1,i,m)*j);
    int p3=(gx(i+1,1,n,j)-gs(i+1,1,n,j)*i)+(gs(i+1,1,n,j)*j-gy(i+1,1,n,j));
    int p4=(gx(i+1,j+1,n,m)-gs(i+1,j+1,n,m)*i)+(gy(i+1,j+1,n,m)-gs(i+1,j+1,n,m)*j);
    int res=p1+p2+p3+p4;
    if (res<ans) ans=res;
    
    printf("%d %d %d %d   %d,",p1,p2,p3,p4,res);
    }
    printf("\n");
    }
    printf ("%d\n",ans);
    }
    return 0;
}

(这其实是我抄的榜一的代码,见谅

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值