博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

愤怒的小鸟 —— 状压DP

题目描述

给定\(N\)个点,和形式抛物线\(y = ax^2 + bx\),每次可以消去抛物线上的点,问消去所有点的最少抛物线数时是多少?

范围

\(N \leq 18\)

题解

题目相当于dancing links思想,用\(n^2\)个抛物线去覆盖点集,那么设\(f_i\)表示覆盖了第\(i\)列的最少行数是多少,转移:

\(f_{i|j} = min(f_i + 1,f_{i|j})\)

预处理经过任意两点的抛物线所覆盖集合即可。

复杂度\(O(T(n^3 + n2^n))\)

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 18;
const double eps = 1e-8;
struct node {
	double x,y;
}pos[N];

int cmp(double x,double y) {
	if(fabs(x - y) < eps) return 0;
	if(x < y) return -1;
	return 1;
}
int n,m;
int path[N][N];
int f[1 << N];
int main () {
	int T;
	cin >> T;
	while(T --) {
		cin >> n >> m;
		for(int i = 0;i < n; ++i) {
			cin >> pos[i].x >> pos[i].y;
		}
		memset(path,0,sizeof path);
		for(int i = 0; i < n; ++i) {
			path[i][i] = 1 << i;
			for(int j = 0;j < n; ++j) {
				double x1 = pos[i].x;
				double x2 = pos[j].x;
				double y1 = pos[i].y;
				double y2 = pos[j].y;
				if(!cmp(x1,x2)) continue;
				double a = (y1 / x1 - y2 / x2) / (x1 - x2);
				double b = y1 / x1 - a * x1;
				if(cmp(a,0) >= 0) continue;
				int sta = 0;
				for(int k = 0;k < n; ++k) {
					double xx = pos[k].x;
					double yy = pos[k].y;
					if(!cmp(a * xx * xx + b * xx,yy)) {
						sta += 1 << k;
					}
				}
				path[i][j] = sta;
			}
		}
		memset(f,0x3f,sizeof f);
		f[0] = 0;
		for (int i = 0;i < (1 << n) - 1; ++i) {
			int tmp = 0;
			for(int j = 0;j < n; ++j) {
				if(!(i >> j & 1)) {
					tmp = j;
					break;
				}
			}
			for(int j = 0;j < n; ++j) {
				f[i | path[tmp][j]] = min(f[i] + 1,f[i | path[tmp][j]]);
			}
		}
		cout << f[(1 << n) - 1] << endl;
	}
	return 0;
}
posted @ 2021-08-21 10:52  Allorkiya  阅读(42)  评论(0编辑  收藏  举报