524. 愤怒的小鸟(状压DP&重复覆盖问题)
作者:互联网
题目:https://www.acwing.com/problem/content/526/
题意:给你一些点,求最少需要经过原点的抛物线经过所有点。
题解:次抛物线经过原点,所以简单推一下抛物线公式,可以发现,只要两个点就能确定一条抛物线,,,,,,所以枚举任何俩个点确定的抛物线,看这个抛物线都经过哪些点,用二进制存储下来经过的点1代表经过这个点,,(先用dfs思维理解下,例如,当你跑完10010这个情况后,以后的搜索再搜到这个情况,其实就不用往下搜了,因为这个情况往后面继续跑的情况你已经跑过一遍了,所以第一次跑完后记录下,再跑到这个情况直接返回记录值就可以了,dfs+记忆化),,dp思路就是从0开始枚举状态,,然后找到这个状态第一个为0的点,记录这个点的位置,然后枚举所有经过这条点的直线(至少把这个位置补成1),记录下目前状态或上这条线够成的状态需要抛物线的数量。
y总讲解:
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
//#include <unordered_map>
//#include <unordered_set>
//#include <bits/stdc++.h>
//#define int long long
#define pb push_back
#define PII pair<int, int>
#define mpr make_pair
#define ms(a, b) memset((a), (b), sizeof(a))
#define x first
#define y second
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 18, M = 1 << 18;
const double eps = 1e-8;
using namespace std;
typedef pair<double, double> PDD;
int n, m;
PDD q[N];//记录点
int path[N][N]; //俩个点构成一 条直线,存的是这条直线经过的点压完后的二进制数
int f[M]; //dp
int cmp(double x, double y) {
if (fabs(x - y) < eps) return 0;
if (x < y) return -1;
return 1;
}
signed main(int argc, char const *argv[]) {
int T;
cin >> T;
while (T--) {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> q[i].x >> q[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 = q[i].x, y1 = q[i].y;
double x2 = q[j].x, y2 = q[j].y;
if (!cmp(x1, x2)) continue;
double a = (y1 / x1 - y2 / x2) / (x1 - x2); //推出的抛物线公式
double b = y1 / x1 - a * x1;
if (a >= 0) continue; //保证抛物线开口向下
int state = 0;
for (int k = 0; k < n;
k++) { //找以这俩个点为曲线的线都经过哪些点,压缩成二进制
double x = q[k].x, y = q[k].y;
if (!cmp(a * x * x + b * x, y)) state += 1 << k;
}
path[i][j] =
state; //存下这个二进制状态,每一位是1代表这条线经过这个点
}
}
memset(f, 0x3f, sizeof(f));
f[0] = 0;
//这里跑的状态,所以每一位0至少都会被枚举一遍
for (int i = 0; i < 1 << n; i++) { //枚举所有状态
int x = 0;
for (int j = 0; j < n; j++) { //找到这个状态的第一个0的点
if (!(i >> j & 1)) {
x = j; //记录下位置
break;
}
}
for (int j = 0; j < n;
j++) { //枚举经过这个点的所有线,把所有状态都更新
f[i | path[x][j]] = min(f[i | path[x][j]], f[i] + 1);
}
}
cout << f[(1 << n) - 1] << endl;
}
return 0;
}
标签:x1,int,double,状压,524,抛物线,include,DP,define 来源: https://blog.csdn.net/Are_you_ready/article/details/117380053