CCSP题解---2016第一题选座

50分思路

没什么好说的, 就十个测试点,看看前六个范围, 发现只要暴力跑一趟就能拿六十分,直接骗,后面的如果说要ac,性价比着实不高

具体思路: 
		一共 k * k 个座位,我们就将每行分为左右两边,每次只需要循环判断放哪一行的距离最小,
		每次就尽量往中间放,可以保证每次是每行的最小距离(这里不做推导证明)
#include <bits/stdc++.h>

using namespace std;
typedef vector<bool> vi;
// L[i]表示第i行左半边,R则表示右半边
long long L[300005], R[300005];
//xc, yc  表示中中心坐标
long long xc, yc, n, k;
long long dis(int i, int x, int LR) {
  long long res = 0;
	//LR=1表示选左边座位,0表示右边座位
  if (LR) {
    if (L[i] < x) return 1e18;
		//循环计算距离
    for (int j = L[i] - x + 1; j <= L[i]; j++) res += abs(j - xc) + abs(i - yc);
  } else {
    if (R[i] < x) return 1e18;
		//循环计算距离
    for (int j = k - R[i] + 1; j <= k - R[i] + x; j++)
      res += abs(j - xc) + abs(i - yc);
  }
  return res;
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  while (cin >> n >> k) {
    xc = yc = k / 2 + 1;
    for (int i = 1; i <= k; i++) L[i] = k / 2 + 1, R[i] = k / 2;
    for (int i = 1; i <= n; i++) {
      int x;
      cin >> x;
      long long res = 1e18, resL = 1e18, resR = 1e18, resI = 1e18;
      for (long long j = 1; j <= k; j++) {
        if (L[j] == k / 2 + 1 && R[j] == k / 2) {
					//如果中间没有被用过,x就放中间,分奇偶讨论
					//奇数直接放正中间,偶要往前放一个(自行推导证明
          if (x & 1) {
            long long disL = dis(j, x / 2 + 1, 1), disR = dis(j, x / 2, 0);
            if (res > disL + disR) {
              res = disL + disR, resI = j;
              resL = L[j] - x / 2;
              resR = xc + x / 2;
            }
          } else {
            long long disL = dis(j, x / 2 + 1, 1), disR = dis(j, x / 2 - 1, 0);
            if (res > disL + disR) {
              res = disL + disR, resI = j;
              resL = L[j] - x / 2;
              resR = xc + x / 2 - 1;
            }
          }
        } else {
					//如果中间被占了,只能放在两边,直接判断即可
          long long disL = dis(j, x, 1), disR = dis(j, x, 0);
          if (res > disL) {
            res = disL, resI = j;
            resL = L[j] - x + 1;
            resR = L[j];
          }
          if (res > disR) {
            res = disR, resI = j;
            resL = k - R[j] + 1;
            resR = k - R[j] + x;
          }
        }
      }
      if (resI != 1e18) {
        cout << resI << " " << resL << " " << resR << endl;
        if (resL >= k / 2 + 2)
          R[resI] -= x;
        else if (resR <= k / 2 + 1)
          L[resI] -= x;
        else {
          if (x & 1)
            L[resI] -= x / 2 + 1, R[resI] -= x / 2;
          else
            L[resI] -= x / 2 + 1, R[resI] -= x / 2 - 1;
        }
      } else
        cout << -1 << endl;
    }
  }
}

60分

long long dis(long long i, long long x, int LR) {
  long long res = 0;
  if (LR) {
    if (L[i] < x) return 1e18;
    else return abs(xc - L[i]) * x + (x - 1) * x / 2 + abs(i - yc) * x;
  } else {
    if (R[i] < x) return 1e18;
    else return abs(xc - R[i]) * x + (x - 1) * x / 2 + abs(i - yc) * x;
  }
  return res;
}

可以推导50分处的dis函数可以直接用O(1)式子算出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值