NOIP2016 提高组 愤怒的小鸟

news/2024/11/17 18:53:38/文章来源:https://www.cnblogs.com/superl61/p/18550898

NOIP2016 提高组 愤怒的小鸟

比较板的状压dp,结果做了3天才写完。

算法一

暴力搜索所有猪的分组情况,同组要满足能一根抛物线打完。时间复杂度 \(O(n^n \times n)\),实现的好的话大概能过 \(60pts\)。最难写的大概是函数判断的部分。想一次写对就一定要打好草稿先理清思路。这是经验之谈。

算法二

知道怎么写函数判断之后,就熟悉了这个问题,于是开始大胆起来想状压。

定义:记 \(f[s]\) 表示打死的猪的集合为 \(s\),最少用几只鸟。

转移:我们找一个 \(s\) 的子集 \(t\),表示最新一步打死 \(t\) 这个集合里的猪(一步),能否一步打死 \(t\) 中所有猪可以 \(O(2^n)\) 预处理 \(g[t]\)。有转移:

\[f[s] = \min(f[s - t] + 1, f[s]) \]

初始化 \(f[0] = 0\),答案是 \(f[2^n - 1]\)

时间复杂度 \(O(3^n)\)。其实复杂度是错的,但是实际发现最卡常的是预处理 \(g[s]\) 的地方(double 实在太慢),然后发现把 fabs 去掉就能从 \(85 \to 100pts\)

#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;
}

算法三

发现只有 \(O(n^2)\) 条抛物线,考虑预处理出每条线能打死的猪的集合,记为 \(line[i]\),有转移:

\[f[s | line[i]] = \min(f[s] + 1, f[s | line[i]]) \]

直接做就是 \(O(2^nn^2)\) 的。

考虑用类似 CF11D - A Simple Task 的 trick,钦定该次选定的抛物线必须经过 \(s\) 中最低位 \(0\) 对应位置的那只猪。可以用 __lg(lowbit(~x)) 来快速查找。时间复杂度降为 \(O(2^nn)\)

#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;
}

总结

自己没能观察出来并利用 “只有 \(n^2\) 条抛物线这个性质”,导致本题竟然耗了 3 天才写完。感觉如果在草稿纸上手玩一下抛物线,应该会更容易发现。

但你的思维已经越来越敏锐了!

加油!

(第100篇公开博客!)

“人的脆弱和坚强都超乎自己的想象,有时候因为一句话就泪流满面,有时候回过头,发现自己已经走过了很长的路。”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/835468.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 内核如何装载和启动一个可执行程序

张晓攀+原创作品转载请注明出处+《Linux内核分析》MOOC课程https://mooc.study.163.com/course/1000029000 实验七——Linux 内核如何装载和启动一个可执行程序 一、实验过程 1.从github上下载相关代码2.然后用test_exec.c 替换test.c,再重新编译生成根文件系统3.启动调试内核…

java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现

java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现@目录背景说明前言Runtime.exec()常见的几种陷阱以及避免方法陷阱1:IllegalThreadStateException陷阱2:Runtime.exec()可能hang住,甚至死锁陷阱3:不同平台上,命令的兼容性陷阱4:错把Runtime.exec()的…

昆工891数据库系统原理强化课程

--昆工昆明理工大学、计算机技术、人工智能、软件工程、网络空间安全、891计算机专业核心综合、计算机系统结构、计算机软件与理论、网络与信息安全、计算机应用技术、综合程序设计、通信工程、817信号与系统、信号与信息处理、通信与信息系统

第7篇Scrum博客

1.站立式会议 1.1 会议照片1.2 会议内容 昨天已完成的工作: 昨天已基本实现用条形图,折线图,饼图展示数据界面功能。 今天计划完成的工作项目模块 需要实现的功能 负责人 预计用时主界面模块 整合代码,查漏补缺 王伊若 5h主界面模块 主界面设计 王伊若 2h主界面模块 查询界…

Ant Design Vue组件安装

https://www.antdv.com/docs/vue/getting-started-cn

书生共学大模型实战营L1G6000 XTuner微调

任务描述:使用XTuner微调InternLM2-Chat-7B实现自己的小助手认知 该任务分为数据集处理、微调训练、合并部署三个环节。数据处理:主要是将目标json文件中的字段替换为和自己用户名相关的字段,这里我们将“尖米”替换为“科研狗1031”:微调训练:采用教程中的XTuner框架,在…

request to https://registry.npm.taobao.org/ant-design-vue failed, reason: certificate has expire

一、原因分析 其实早在 2021 年,淘宝就发文称,npm 淘宝镜像已经从 http://registry.npm.taobao.org 切换到了 http://registry.npmmirror.com。旧域名也将于 2022 年 5 月 31 日停止服务(直到 HTTPS 证书到期才真正不能用了)2024年1 月 22 日,淘宝原镜像域名(http…

【学校训练记录】11月个人训练赛4个人题解

A题意可以理解为在a,b的范围内如果一个数是某个整数的立方,求与其距离为k的范围内有几个整数的平方数,我们可以对于每个立方数求出其数量,注意边界问题 #include <bits/stdc++.h> #define int long long using namespace std;int a, b, k; void solve(){cin >>…

第六篇Scrum博客

1.站立式会议 1.1 会议照片1.2 会议内容 昨天已完成的工作: 已经完成了账目的查询界面功能,按日期、备注以及收入支出查询等功能。 今天计划完成的工作项目模块 需要实现的功能 负责人 预计用时主界面模块 协助他人完成工作 王伊若 2h主界面模块 分类报告界面 王伊若 3h主界面…

学校个人训练记录

A题意可以理解为在a,b的范围内如果一个数是某个整数的立方,求与其距离为k的范围内有几个整数的平方数,我们可以对于每个立方数求出其数量,注意边界问题 #include <bits/stdc++.h> #define int long long using namespace std;int a, b, k; void solve(){cin >>…

Scrum 冲刺博客-day2

一、每天会议 昨天完成的任务与今天计划完成任务成员 昨天已完成任务 今天计划完成任务董雯霖 组织会议,确立各自工作 用户注册页面陈金星 参会,发表意见 用户登录页面邱列圻 参会,发表意见 用户模块的接口开发李嘉远 参会,发表意见 页面测试詹洛熙 参会,发表意见 接口测试…