NOIP2016 提高组 愤怒的小鸟
比较板的状压dp,结果做了3天才写完。
算法一
暴力搜索所有猪的分组情况,同组要满足能一根抛物线打完。时间复杂度
算法二
知道怎么写函数判断之后,就熟悉了这个问题,于是开始大胆起来想状压。
定义:记
转移:我们找一个
初始化
时间复杂度 double
实在太慢),然后发现把 fabs
去掉就能从
#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=(r);++i)
#define G(i,r,l) for(int i(r);i>=(l);--i)
using namespace std;
using ll = long long;
const int N = 20;
const double eps = 1e-8;
int T, n, m, ans;
int a[N], f[(1 << 18) + 5];
bool g[(1 << 18) + 5];
double x[N], y[N];
bool check(int num){
double Y1 = y[a[1]], X1 = x[a[1]], Y2 = y[a[2]], X2 = x[a[2]];
if(X1 == X2) return 0;
double fz = Y2 - (Y1 * X2) / X1, fm = X2 * (X2 - X1);
if(fz == 0 || fm == 0) return 0;
double A = fz / fm;
if(A >= 0) return 0;
if(num > 2){
double B = (Y2 - A * X2 * X2) / X2, X = x[a[num]], Y = y[a[num]], ret = A * X * X + B * X - Y;
if(ret > eps || ret < -eps) return 0;
}
return 1;
}
void dfs(int nw, int S, int num, bool flag){
if(num > 1 && flag) flag = check(num);
g[S] = flag;if(nw > n) return ;
a[num + 1] = nw;
dfs(nw + 1, S | (1 << (nw - 1)), num + 1, flag);
dfs(nw + 1, S, num, flag);
}
void Main(){
cin >> n >> m; F(i, 1, n) cin >> x[i] >> y[i];
dfs(1, 0, 0, 1), f[0] = 0;
F(s, 1, (1 << n) - 1){
f[s] = n;
for(int t = s; t >= 0; t = (t - 1) & s){
if(g[s - t] && f[t] + 1 < f[s]) f[s] = f[t] + 1;
if(!t) break;
}
} return cout << f[(1 << n) - 1] << '\n', void();
}
signed main(){
// freopen("ex_angrybirds3.in","r",stdin);
// freopen("angrybirds.out","w",stdout);
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> T; while(T --) Main();
return fflush(0), 0;
}
算法三
发现只有
直接做就是
考虑用类似 CF11D - A Simple Task 的 trick,钦定该次选定的抛物线必须经过 __lg(lowbit(~x))
来快速查找。时间复杂度降为
#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=(r);++i)
#define G(i,r,l) for(int i(r);i>=(l);--i)
#define lowbit(x) (-x & x)
using namespace std;
using ll = long long;
const int N = 20;
const double eps = 1e-8;
int T, n, m, ans, cnt = 0;
int a[N], f[(1 << 18) + 5], line[N * N];
double x[N], y[N];
vector<int> g[N];
void init(){
F(i, 1, cnt) line[i] = 0;
cnt = 0;
F(i, 1, n){
F(j, 1, n){
if(i == j) {
line[++ cnt] |= (1 << (i - 1));
continue;
}
double Y1 = y[i], X1 = x[i], Y2 = y[j], X2 = x[j];
if(X1 == X2) continue;
double fz = Y2 - (Y1 * X2) / X1, fm = X2 * (X2 - X1);
if(fz == 0 || fm == 0) continue;
double A = fz / fm;
if(A >= 0) continue;
++ cnt;
line[cnt] |= (1 << (i - 1));
line[cnt] |= (1 << (j - 1));
double B = (Y2 - A * X2 * X2) / X2;
F(k, 1, n){
if(k == i || k == j) continue;
double X = x[k], Y = y[k], ret = A * X * X + B * X - Y;
if(ret > eps || ret < -eps) continue;
line[cnt] |= (1 << (k - 1));
}
}
}
F(i, 1, n) F(j, 1, cnt) if((line[j] >> (i - 1)) & 1) g[i].push_back(j);
}
void Main(){
cin >> n >> m; F(i, 1, n) cin >> x[i] >> y[i];
init(); f[0] = 0;
F(s, 1, (1 << n)) f[s] = n;
F(s, 0, (1 << n) - 1) {
int x = lowbit(~s);
x = __lg(x) + 1;
for(auto i : g[x]) f[s | line[i]] = min(f[s] + 1, f[s | line[i]]);
}
return cout << f[(1 << n) - 1] << '\n', void();
}
signed main(){
// freopen("ex_angrybirds1.in","r",stdin);
// freopen("angrybirds.out","w",stdout);
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> T; while(T --) Main();
return fflush(0), 0;
}
总结
自己没能观察出来并利用 “只有
但你的思维已经越来越敏锐了!
加油!
(第100篇公开博客!)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2023-11-17 【笔记】 STL容器