[日常训练] 坦克

【问题描述】
   小H有一位朋友叫WJ,他非常喜欢坦克,我们亲切的称他“坦克之王”。

  后来WJ发现有很多人也很喜欢坦克,并且他们举办了一个关于坦克的比赛,具体规则如下:
  地上的一块区域被N+1条横线和M+1条竖线(横线竖线编号从左往右从上往下从0开始到N+1和M+1)划分成了N*M块,每一块都是边长为4的正方形。每个正方形的中心放了一辆坦克,每辆坦克都有一个权值,现在裁判要求所有玩家选定一个位置(必须位于横线和竖线的交叉点上),然后计算得分,谁的得分小谁就胜出。得分的计算为参赛者所在位置与每辆坦克所在位置直线距离的平方和坦克权值乘积的和。具体如图(图在最后)。
  WJ想在这次比赛中获胜,已捍卫他坦克之王的地位,请你帮帮他。

【输入格式】

  第一行两个整数N,M。
  下面N行每行M个数表示格子中坦克的权值。

【输出格式】

  第一行一个数字表示最小的得分。
  第二行两个数字空格隔开分别表示WJ所在位置横线的编号和竖线的编号。

【输入输出样例】

样例输入1(tank.in)
2 3
3 4 5
3 9 1

样例输出1(tank.out)
392
1 1

样例输入2(tank.in)
3 4
1 0 0 0
0 0 3 0
0 0 5 5

样例输出2(tank.out)
240
2 3

【样例说明】

得分3*8+3*8+4*8+9*8+5*40+1*40=392.
可以证明是最小的。

【数据范围】

对于40%的数据,1<=N,M<=100;
对于100%的数据,1<=N,M<=1000,坦克的权值大于0且不超过100000;

【50分】 O(n4) 暴力枚举(可考虑最优性剪枝)

【100分】乱搞???

记坦克 i 所在位置为(xi,yi) WJ 所在位置为 (tx,ty) ,坦克 i 的权值为wi。则题目所求即为

Answer=[(xitx)2+(yity)2]×wi

=(xitx)2×wi+(yity)2×wi

【解法1】

分别考虑 (xitx)2×wi (yity)2×wi
对答案的影响(两者是互相独立的),因为每一行(列)的 yi(xi) 值是相等的,我们先算出每一行(列)的 Wi 之和,然后 O(n2) 枚举 xi,tx yi,ty ,计算对答案的影响来更新最优方案。

【代码1】
#include <iostream>
#include <cstdio>

using namespace std;
typedef long long ll;
const int N = 1005;
const ll Maxn = 1000000000000000000ll;

ll sqr[N << 1], sx[N], sy[N], Ansx = Maxn, Ansy = Maxn;
int n, m, Kx, Ky, a[N][N];

inline int Max(const int &x, const int &y) {return x > y ? x : y;}

inline int get()
{
    char ch; int res = 0;
    while ((ch = getchar()) < '0' || ch > '9');
    res = ch - '0';
    while ((ch = getchar()) >= '0' && ch <= '9')
     res = (res << 3) + (res << 1) + ch - '0';
    return res; 
}

inline ll Sqr(const ll &x) {return x * x;}

int main()
{
    freopen("tank.in", "r", stdin);
    freopen("tank.out", "w", stdout);
    n = get(); m = get();
    for (int i = 1; i <= n; ++i)
     for (int j = 1; j <= m; ++j) 
     {
        int x = get();
        sx[i] += x; sy[j] += x;
     }
    for (int i = 0; i <= n; ++i)
    {
        ll sum = 0;
        for (int j = 1; j <= n; ++j) 
         sum += Sqr((ll)(i - j << 2) + 2) * sx[j]; 
        if (sum < Ansx) Ansx = sum, Kx = i; 
    }
    for (int i = 0; i <= m; ++i)
    {
        ll sum = 0;
        for (int j = 1; j <= m; ++j) 
         sum += Sqr((ll)(i - j << 2) + 2) * sy[j];
        if (sum < Ansy) Ansy = sum, Ky = i;
    }
    cout << Ansx + Ansy << endl << Kx << " " << Ky << endl;
    fclose(stdin); fclose(stdout); 
}
【解法2】

继续对上述式子进行分解

=(xitx)2×wi+(yity)2×wi

=(x2i2xitx+tx2)×wi+(y2i2yity+ty2)×wi

=(x2i+y2i)×witx×2xi×wity×2yi×wi

+(tx2+ty2)×wi

那么除了 tx,ty tx2+ty2 ,其余的我们都是可以预处理的,也就是说只要枚举 WJ 所在位置 (tx,ty) ,就可以直接算出答案,复杂度同样为 O(n2)

【代码2】
#include <iostream>
#include <cstdio>

using namespace std;
typedef long long ll;
const int N = 1005;
const ll Maxn = 1000000000000000000ll;

ll Ans = Maxn, s1, s2, s3, s4; 
int n, m, Kx, Ky, a[N][N];

inline int Max(const int &x, const int &y) {return x > y ? x : y;}

inline int get()
{
    char ch; int res = 0;
    while ((ch = getchar()) < '0' || ch > '9');
    res = ch - '0';
    while ((ch = getchar()) >= '0' && ch <= '9')
     res = (res << 3) + (res << 1) + ch - '0';
    return res; 
}

inline ll Sqr(const ll &x) {return x * x;}

int main()
{
    freopen("tank.in", "r", stdin);
    freopen("tank.out", "w", stdout);
    n = get(); m = get();
    for (int i = 1; i <= n; ++i)
     for (int j = 1; j <= m; ++j) 
     {
        ll dx = (i - 1 << 2) + 2,
           dy = (j - 1 << 2) + 2,
            w = (ll)get();  
        s1 += (Sqr(dx) + Sqr(dy)) * w;
        s2 += (dx << 1) * w, s3 += (dy << 1) * w;
        s4 += w;
     }
    for (int i = 0; i <= n; ++i)
     for (int j = 0; j <= m; ++j)
     {
        ll tx = (ll)(i << 2), ty = (ll)(j << 2),
           tmp = s1 - s2 * tx - s3 * ty
               + s4 * (Sqr(tx) + Sqr(ty));
        if (tmp < Ans) Ans = tmp, Kx = i, Ky = j; 
     }
    cout << Ans << endl << Kx << " " << Ky << endl;
    fclose(stdin); fclose(stdout); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值