(中途相遇法)Uva 1618 Weak Key(分段DP+枚举)

枚举四个肯定是不行的,所以要枚举两个,我选择枚举中间的两个数,一个最大,一个最小。

另外,两种情况如果将数组翻转过来就可以相互等效,所以我只考虑第一种——2413 (从小到大排序)

为什么称为分段呢。。。你将会看到这是有道理的。

设l[i][j] 是DP式,若i小于j,可分为data【i】< data【j】与 data【i】> data【j】两种,

若i 大于 j,又可分为data【i】< data【j】与 data【i】> data【j】两种。并且这些情况都能在同一个二维数组里实现。

定义: L[i][j] 若i小于j并且data[i]>data[j],L[i][j]为下标小于i并且data值位于i与j之间的最小值的下标,如果没有就标为零。

若i小于j并且data[i]<data[j],L[i][j]为下标小于i并且data值比j大的最小值的下标,如果没有就标为零。


当i大于j时也类似定义(想一想吧...)


所以Dp过程有点复杂,不多说了,自己看代码。

这样只要枚举i 和 j (i<j,data[i]>data[j])在查询L[i][j] 和 L[j][i],分别表示的是下标小于i的data值(满足大于data[j] 并小于data[i]的)最小的 下标 和 下标大于j的 data

值(满足大于data[j] 并小于data[i]的)最大值的下标,在比较两个值的大小。。

AC代码(可能是uva上最好的算法,就是有点复杂。Run time 和第一的一样啊,都是0.016s)

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

const int Main = 5000 + 50;
int l[Main][Main],data[Main];
int G() {
	char ch;
	int u(0);
	do ch=getchar(); while(!isdigit(ch) );
	u = ch - 48;
	while(isdigit(ch=getchar())) u = u*10 + ch-48;
	ungetc(ch,stdin);
	return u;
}
int main()
{
	int T, n;
	T = G();
	while(T--) {
		bool rev(0);
		int p,q;
		n = G();
		for(int i(1); i <= n; i++) data[i] = G();
		for(int i=1;i<=n; i++) l[1][i] = l[n][i] = 0;
		do {
			for(int i = 2; i <= n-2; i++) 
			    for(int j = i+1; j <= n-1; j++)
			{
				if(data[i] < data[j]) 
				{
					l[i][j] = l[i-1][j] ? l[i-1][j] : data[i-1] > data[j] ? i-1 : 0;
				}else {
					if(data[j] < data[i-1] && data[i-1] < data[i]) {
						l[i][j] = l[i-1][j] ? l[i-1][j] : i-1;
					}else {
						if(!l[i-1][j]) l[i][j] = 0;
						else l[i][j] = data[l[i-1][j]] < data[i] ? l[i-1][j] : 0;
					}
				}
			}
			for(int i = n-1; i >= 3; i--)
				for(int j = i-1; j >= 2; j--) 
			{
				if(data[i] > data[j]) 
				{
					l[i][j] = l[i+1][j] ? l[i+1][j] : data[i+1] < data[j] ? i+1 : 0;
				}else {
					if(data[i] < data[i+1] && data[i+1] < data[j])  
						l[i][j] = l[i+1][j] ? l[i+1][j] : i+1;
					else {
						if(!l[i+1][j]) l[i][j] = 0;
						else l[i][j] = data[l[i+1][j]] > data[i] ? l[i+1][j] : 0; 
					}
				}
			}
			for(int U = 2; U <= n-2; U++) 
			    for(int V = U+1; V <= n-1; V++)
			{
				if(data[U] < data[V]) continue;
				if(!l[U][V]) continue; else p = l[U][V]; 
				if(!l[V][U]) continue; else q = l[V][U];
				if(data[p] < data[q]) goto ext;	
			}
			rev^=1;
			if(rev) reverse(data+1,data+1+n);
		}while(rev);
		printf("NO\n");
		continue;
		ext:;
		printf("YES\n");
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值