UTPC 2011 G: プログラミングコンテストチャレンジブック

問題概要

N(<10^5)個の長さの棒がある。このうち6本を選んで三角形を二つ作りたい。辺の長さの和を最大化する問題。

解法

三角形が1つなら長さをソートして連続する3つを順に見ていけば良い。なので、問題を分割する。棒を半分に分けてそれぞれ三角形を1個ずつ作る。棒を半分に分けたとき、ただしく分割される確率は1/32なので、試行回数を増やせば何とか通りそう。
で、通る確率について考えてみよう。まず、テストケース1個あたりの反復回数をKとする。また、テストケースの個数をT個とすると、全て通る確率は(1-(1-1/32)^K)^T。これにT=70を代入してプロットしてみればわかるけど、K=100~200の範囲で急激に増加していることがわかる。K=100だと落ちる確率の方が高いのだけど何とかAcceptされた。
想定解法はもっと綺麗にgreedyで解いている。

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

typedef long long int64;

int64 as[100009];
int N;

inline bool canMake(int64 a, int64 b, int64 c){
	return a<b+c;
}

int64 solve(){
	vector<int64> cur;
	for(int i=0; i<N; i++){
		cur.push_back(as[i]);
	}
	int64 ret = 0;
	int len  = N/2;
	for(int loop=0; loop<100; loop++){
		random_shuffle(cur.begin(), cur.end());
		vector<int64> bs, cs;
		for(int i=0; i<len; i++)bs.push_back(cur[i]);
		for(int i=len; i<N; i++)cs.push_back(cur[i]);
		int64 a = -1, b = -1;
		sort(bs.rbegin(),bs.rend());
		for(int i=0; i<(int)bs.size()-2; i++){
			if(canMake(bs[i],bs[i+1],bs[i+2])){
				a = bs[i] + bs[i+1] + bs[i+2];
				break;
			}
		}
		if(a==-1) continue;
		sort(cs.rbegin(), cs.rend());
		for(int i=0; i<(int)cs.size()-2; i++){
			if(canMake(cs[i],cs[i+1],cs[i+2])){
				b = cs[i] + cs[i+1] + cs[i+2];
				ret = max(ret, a + b);
				break;
			}
			else if(3*cs[i] < ret - a) break;
		}
	}
	return ret;
}

int main(){
	scanf("%d",&N);
	for(int i=0; i<N; i++){
		scanf("%lld",as+i);
	}
	printf("%lld\n",solve());
	return 0;
}