bzoj4985(2점+dp)

1908 단어 dp이분
평점을 작은 것부터 큰 것까지 순서를 정하고 모든 평점에서 하나의 답안 x를 나누어 x가 합법적인지 검증한다.
p[i]: i가 마지막 수일 때, 그리고 i>=x, 앞에 추가해야 할 >=x의 수;
만약 dp[마지막 개수]가 필요로 하는 >=x의 개수 <=위치의 수를 모르면 합법적이다.
dp[1-n]는 모두 최초의 상태이다.
만약 i 위치의 수가 =x라면, 값은 inf입니다
만약 i 위치의 수가 >=x이면 그 자체가 >=x이기 때문에 값은 0이다.
i 위치의 수를 알 수 없으면 1을 부여합니다.
그 다음에 시뮬레이션을 해서 매번 팀의 첫 번째 세 개를 뽑고 가장 작은 두 개의 수를 합쳐서
만약 두 수의 화=x의 수가 있다면 새로운 팀의 끝도 반드시 >=x가 될 수 있다.dp[++tail] = 두 수의 합
하면, 만약, 만약...

#include
#include
#include
#include
using namespace std;
int a[200000], b[200000],c[200000], n, m;
long long dp[600000]; 
int check(int x)
{
	memset(dp,0,sizeof(dp));
	int shu  = 0;
	for(int i = 1; i <= n - m; i++)
	{
		if(b[i] >= x) shu ++;
	}
	for(int i = 1;  i <= n; i++)
	{
		if(a[i] == 0) dp[i] = 1;
		else if(a[i] >= x) dp[i] = 0;
		else dp[i] = 1e9;
	}
	int head = 1, tail = n;
	while(head + 1< tail)
	{
		long long t = dp[head + 1] + dp[head] + dp[head + 2];
		long long ha = max(dp[head + 1], dp[head]);
		 ha = max(dp[head + 2], ha);
       if(t - ha > 1e9)dp[++tail] = 1e9;
	  else dp[++tail] = t - ha;
       head += 3;
	}
	if(dp[tail] > shu) return 0;
	return 1;
}
int main()
{
	cin >> n >> m;
	int cnt = 0;
	for(int i = 1; i <= m; i++)
	{
		int d, v;
		scanf("%d%d",&v,&d);
		a[d] = v;
		c[++cnt] = v;
	}
	for(int i = 1; i <= n - m; i++)
	{
    	scanf("%d",&b[i]);
		 c[++cnt] = b[i];
	}
	sort(1+c,1+c+cnt);
	int ans = 0,l = 1, r = cnt;
	while(l <= r)
	{
		int mid = (l + r) / 2;
		if(check(c[mid]) == 1)
		{
			l = mid + 1;
			ans = c[mid];
		}
		else r = mid - 1;
	}
	cout << ans;
	return 0;
}

 

좋은 웹페이지 즐겨찾기