另类的状态转移方式
另类的状态转移方法
一、状态机
简述:一系列有序的事件,把点扩展成一个过程
初始化技巧:
初始化只有两种情况,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 ;
}

浙公网安备 33010602011771号