【题解】[POI2007] SKA-Rock Garden

题意

给你 n 块石头的坐标 (x[i],y[i]) ,可以对任意一块石头交换横纵左边,代价为 w[i] ,求最小的代价使得 Max(x[i]-x[j])+Max(y[i]-y[j]) 最小。

Solution:

首先考虑当什么时候取到最小值。

结论:当所有石子都在 y=x 一侧时最优。
请添加图片描述

证明:首先考虑 n=2 的情况,对于任意 i 满足 x[i]<y[i] 。此时 Max(x[i]-x[j])+Max(y[i]-y[j]) 最小。对于 n+1 的情况,假设前面是这样的:那么取 x[n+1]<y[n+1] 使得答案最小。证毕。

请添加图片描述

考虑最小化花费。首先 内部的点不能翻,只能让答案变大。

请添加图片描述
那么现在有四种情况,对于每种情况,可以贪心 O(n) 判断每个点是否翻转。

请添加图片描述
请添加图片描述

请添加图片描述

请添加图片描述
实际上我们可以把四种情况放到一个矩形里,也就是说用左上角那个矩形就够了。(说明一下,我这里探究出来有 6 种情况,不过 2,3 种和 1,4 种似乎是同一个矩形)。

时间复杂度 O(n)

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define PII pair<ll,ll>
using namespace std;
const int mx=1e6+5;
int n;
PII one,two;
PII ans;
struct node{
	int x,y,w,op;
}q[mx];
//期望得分 80~100 pts
void check(int x,int x2,int y,int y2) {
	ll res=0;
	for(int i=1;i<=n;i++) {
		if(x<=q[i].x&&q[i].x<=x2&&y<=q[i].y&&q[i].y<=y2) continue;
		if(x<=q[i].y&&q[i].y<=x2&&y<=q[i].x&&q[i].x<=y2) res+=q[i].w;
		else return;
	}
	if(res<ans.second) {
		ans.second=res;
		for(int i=1;i<=n;i++) {
			if(x<=q[i].x&&q[i].x<=x2&&y<=q[i].y&&q[i].y<=y2) q[i].op=0;
			else q[i].op=1;
		}
	}
} 
void solve() {
	int x(INF),y(INF),x2(0),y2(0); 
	for(int i=1;i<=n;i++) {
		scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].w);
		if(q[i].x>q[i].y) {
			swap(q[i].x,q[i].y);
			q[i].op=1;
		}
		x=min(x,q[i].x);
		x2=max(x2,q[i].x);
		y=min(y,q[i].y);
		y2=max(y2,q[i].y); 
		if(q[i].op) {
			swap(q[i].x,q[i].y);
			q[i].op=0;
		}
	}
	ans.first=1ll*(x2-x)*2+(y2-y)*2;
	check(x,x2,y,y2);
	check(x,y2,y,x2);
	check(y,y2,x,x2);
	check(y,x2,x,y2);
}
int main() {
	scanf("%d",&n); ans=make_pair(INF,INF);
	solve();
	printf("%lld %lld\n",ans.first,ans.second);
	for(int i=1;i<=n;i++) printf("%d",q[i].op);
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值