愤怒的小鸟 —— 状压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;
}
原文地址:https://www.cnblogs.com/Akoasm/p/15169062.html