【校内模拟】神光

看到\(“L 的最小值”\),很容易想到二分答案,那么这道题的关键就是如何快速地检验

首先,如果已经规定了操作顺序,我们可以\(O(n)\)贪心求解

但是要枚举顺序的话复杂度是阶乘级别的,显然布星

于是考虑\(DP\),我一开始的\(DP\)状态:\(dp[i][j]\)表示干掉前\(i\)\(fa\)坛,用\(j\)次红光时的最少用多少次绿光

发现转移有些麻烦,又因为时间不大够了,我就随机操作顺序+贪心\(100\)次了(骗到\(60\)分)

题解的做法:

\(dp[i][j]表示用i次\color{green}{绿光}和j次\color{red}{红光}最多摧毁从1开始多少个连续的法坛\)

转移就是

\[dp[i][j]=P[dp[i-1][j]+1]+Q[dp[i][j-1]+1] \]

其中\(P[k]\)表示从第\(k\)个法坛开始向右\(L\)长度内有多少法坛,\(Q[k]\)表示从第\(k\)个法坛开始向右\(2L\)长度内有多少法坛,都可以在\(DP\)前预处理出来

\(60\)分随机算法:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<ctime>
using namespace std;
const int N=2010;
int n,r,g,a[N];
inline bool check(int l){
	int t=100;
	while(t--){
		int tot1=0,tot2=0,dr=0;
		for(int i=1;i<=n;i++){
			if(a[i]<dr) continue;
			if((rand()%2||tot2==g)&&tot1!=r){
				dr=a[i]+l-1; ++tot1;
				if(dr>a[n]) return 1;
			}
			else if(tot2!=g){
				dr=a[i]+l+l-1; ++tot2;
				if(dr>a[n]) return 1;
			}
			else break;
		}
		if(dr>a[n]) return 1;
	}
	return 0;
}
int main()
{
	srand(19260817+time(NULL));
	freopen("light.in","r",stdin);
	freopen("light.out","w",stdout);
	scanf("%d%d%d",&n,&r,&g);
	if(r+g>=n){
		puts("1");
		return 0;
	}
	for(int i=1;i<=n;++i)
		scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	int l=1,r=1000000000;
	while(l<r){
		int mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	printf("%d\n",l);
	fclose(stdin); fclose(stdout);
	return 0;
}

\(100\)分代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 2010
#define reset(a) memset(a,0,sizeof(a))
int n,r,g,a[N],dp[N][N];
int P[N],Q[N];
bool check(int L)
{
	reset(P),reset(Q),reset(dp);
	for(int i=1;i<=n;i++){
		int j=i;
		while(a[j]<=a[i]+L-1&&j<=n) ++j;
		P[i]=j-1;
		while(a[j]<=a[i]+L+L-1&&j<=n) ++j;
		Q[i]=j-1;
	}
	P[n+1]=Q[n+1]=n;
	for(int i=0;i<=r;i++)
	 for(int j=0;j<=g;j++){
	    if(i) dp[i][j]=max(dp[i][j],P[dp[i-1][j]+1]);
	    if(j) dp[i][j]=max(dp[i][j],Q[dp[i][j-1]+1]);
	}
	return dp[r][g]==n;
}
int main()
{
	freopen("light.in","r",stdin);
	freopen("light.out","w",stdout);
	scanf("%d%d%d",&n,&r,&g);
	if(r+g>=n){
		puts("1");
		return 0;
	}
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	int l=1,r=1e9;
	while(l<r){
		int mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	printf("%d\n",l);
	fclose(stdin); fclose(stdout);
	return 0;
}
posted @ 2018-10-16 21:31  yjk  阅读(162)  评论(0编辑  收藏  举报