另类的状态转移方式

另类的状态转移方法

一、状态机

简述:一系列有序的事件,把点扩展成一个过程

初始化技巧:

初始化只有两种情况,0和极值。

1、0表示这个状态合法,可以从这个状态转移过来。

2、极值表示这个状态不合法,并不可以从这个状态转移过来

关于状态机的应用:

状态机可以较清楚的将一个混沌的状态,拆分成若干个清晰的状态。可以将状态之间的转移清楚呈现出来。

例题,

Acwing算法提高课,股票买卖IV和V。

设计密码 此题将状态机与字符串匹配相结合。比较有趣,难度大概可以到提高组。

二、状态压缩

应用:

在状态并不容易表示时,将状态用一个其他进制数来表示,常用二进制。预处理和记忆化是状态压缩型问题所常涉及的基本思路。

有趣的题目

1、愤怒的小鸟(NOIP2016)

本题题目风格很符合 \(noip\) 题目的作风,考察了简单数学,搜索结合状态压缩三个知识点,由于属于精确覆盖问题的模型,\(Dancing Links\) 可做。在 \(Acwing\) 上会将其时间卡满。

首先声明一点,这个题很复杂!

\(sta\) 的含义是由 \(i\)\(j\) 两点确定出的一条抛物线,\(f[x]\)​ 为要到达当前状态需要的最小的抛物线数量。

本题思路极其暴力,第一步需要分析抛物线曲线的性质(初中数学)。以小鸟所在的点为原点构建平面直角坐标系,所有的抛物线都满足 \(y = ax^2 + bx(a < 0)\) 由此,只需要两点即可确定一条抛物线。

随后 \(n^2\) 枚举出所有点对能构成的合法的抛物线 \(i , j\) 分别表示确定出该抛物线的两点,然后 $ O(n)$ 枚举处理出在该抛物线上的所有点,压缩成状态 \(state\) ,放到 \(p\) 中。需要注意的是 可能存在点与其他的任意点都无法构成抛物线,所以需要将 \(p_{ii}\) 初始化成 $2 ^ i $ 。

最关键的一部分 !(自我感觉很妙)

枚举所有状态,在枚举每个状态的时候遍历此状态的二进制的每一位,只要找到一个不为1的位置,break 随后进行状态转移!

这里其实是用的一种递推的思路,在这种方式下,可以证明,在进行状态转移的时候用到的所有之前的状态都已经最优。

所有转移后得出的当前状态的值一定也是最优的。

code

#include <bits/stdc++.h> 
#define int long long 
#define pii pair<double , double > 
#define x first
#define y second 
#define eps 1e-8
using namespace std ;
const int maxn = 1 << 18 ;
int f[maxn] , p[20][20] ; 
pii w[20] ; 
int n , m , cnt ; 
int t ; 
int cmp(double x , double y )
{
	if(fabs(x - y) < eps)	return 0 ; 
	if(y > x)	return -1 ; 
	return 1 ; 
}
signed main()
{
	cin >> t ; 	
	while( t-- )
	{
		cin >> n >> m ;
		for(int i = 0 ; i < n ; i++ )
		{
			double x , y ; 	cin >> x >> y ; 
			w[i] = {x , y} ; 
		}	
		memset(p , 0 , sizeof p )  ;
		for(int i = 0 ; i < n ; i++ )
		{
			int state = 0 ; 
			p[i][i] = 1 << i ; 
			double xo = w[i].x , yo = w[i].y ;
			for(int j = 0 ; j < n ; j++ )
			{
				double xs = w[j].x , ys = w[j].y ;
				if( !cmp(xs , xo) )	continue ;
				double a = (yo / xo - ys / xs) / (xo - xs) ;
				double b = b = yo / xo - a * xo ;
				if( cmp(a , 0) >= 0 ) continue ;
				state = (1 << i) | (1 << j) ;   
				for(int k = 0 ; k < n ; k++ )
				{
					double x = w[k].x , y = w[k].y ;
					if(! cmp(a * x * x + b * x , y) )	state |= (1 << k) ; 
				}
				p[i][j] = state ; 
			} 
		}
		int x = 0 ;
		memset(f , 0x3f , sizeof f ) ;
		f[0] = 0 ; 
		for(int i = 0 ; i + 1 < 1 << n ; i++ )
		{
			for(int j = 0 ; j < n ; j++ )
			{
				if(! ((i >> j) & 1))
				{
					x = j ;
					break ; 
				}
			}
			for(int k = 0 ; k < n ; k++ )
				f[i | p[x][k]] = min(f[i | p[x][k]] , f[i] + 1) ; 
		}
		cout << f[(1 << n) - 1] << '\n' ; 
	} 
	return 0 ; 
}

posted @ 2022-05-12 09:07  Simon_...sun  阅读(45)  评论(0)    收藏  举报