当前位置: 首页 > news >正文

洛谷题单指南-状态压缩动态规划-P2831 [NOIP 2016 提高组] 愤怒的小鸟

原题链接:https://www.luogu.com.cn/problem/P2831

题意解读:求覆盖平面上所有点的最少的曲线数,曲线形如y=ax2+bx

解题思路:

1、两点确定一条曲线

由于曲线方程为:y=ax2+bx,可以判断两点确定一条曲线

设两点为(x1,y1),(x2,y2),则有

y1 = ax12 + bx1,y2 = ax22 + bx2

解方程得到:a = (y1/x1-y2/x2)/(x1-x2),b = y1/x1 - ax1

可以枚举所有两个点构成的曲线,再将所有点代入方程即可确定每条曲线经过了哪些点,每条曲线经过的点可以用状态压缩存入一个整数

设path[i][j]表示第i、j个点构成的曲线经过的所有点的状态

这里有一个特殊情况,如果两个不同的点构成的曲线a不小于0则不合法,如果所有两点构成的曲线都不合法,经过每一个点至少有一个合法的曲线,也

就是path[i][i] = 1 << i,第i个点必有曲线经过i。

2、搜索

dfs(当前已覆盖的点:stat,用了几条曲线:cnt)

{

  if stat表示所有点已覆盖

    ans = min(ans, cnt)

    return

  for 所有当前没有覆盖的点

    对于任意一个没有覆盖的点,找到所有经过它的曲线

      用曲线经过的点的状态 | stat,得到newstat

      继续dfs(newstat, cnt + 1) 

    break,每次只找一个没有覆盖的点,用所有经过它的曲线去更新状态

}

调用:dfs(0, 0)

3、考虑到性能,可以定义f[i]记录已覆盖点状态为i时经过的最少曲线数,使用记忆化搜索

dfs(当前已覆盖的点:stat,用了几条曲线:cnt)

{

  if(f[i] != INF && f[i] <= cnt) return;

  f[i] = min(f[i], cnt)

  if stat表示所有点已覆盖

    ans = min(ans, cnt)

    return

  for 所有当前没有覆盖的点

    对于任意一个没有覆盖的点,找到所有经过它的曲线

      用曲线经过的点的状态 | stat,得到newstat

      继续dfs(newstat, cnt + 1) 

    break,每次只找一个没有覆盖的点,用所有经过它的曲线去更新状态

}

4、注意浮点数的比较不能直接用==,应该相减看是否小于一个更小的值

100分代码:

#include <bits/stdc++.h>
using namespace std;const int N = 18, INF = 0x3f3f3f3f;
const double EPS = 1e-6;
struct Point
{ double x, y; 
} points[N];
int path[N][N]; //path[i][j]表示i,j两个点构成的曲线经过的所有点的状态
int f[1 << N]; //f[i]表示经过的所有点的状态为i时所需的最少曲线数
int t, n, m, ans;int cmp(double x, double y)
{if(fabs(x - y) < EPS) return 0;if(x > y) return 1;else if(x < y) return -1;
}void dfs(int stat, int cnt)
{if(f[stat] <= cnt) return; //记忆化+最优性剪枝f[stat] = min(f[stat], cnt);if(stat == (1 << n) - 1) //所有点都已覆盖{ans = min(ans, cnt);return;}for(int i = 0; i < n; i++){if(!(stat >> i & 1)){for(int j = 0; j < n; j++){if(path[i][j]){int newstat = stat | path[i][j];dfs(newstat, cnt + 1);}}break; //每次取一个未被覆盖的点,用经过该点的不同曲线去更新覆盖状态}}
}int main()
{cin >> t;while(t--){memset(path, 0, sizeof(path));cin >> n >> m;for(int i = 0; i < n; i++){cin >> points[i].x >> points[i].y;}for(int i = 0; i < n; i++){path[i][i] = 1 << i; //每个点一定有一条经过它的曲线for(int j = 0; j < n; j++){double x1 = points[i].x, y1 = points[i].y;double x2 = points[j].x, y2 = points[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) != -1) continue; //a必须小于0//找到曲线y=ax^2+bx经过的所有点for(int k = 0; k < n; k++){double x = points[k].x, y = points[k].y;if(!cmp(y, a * x * x + b * x)){path[i][j] |= (1 << k);}}}}ans = INF;memset(f, 0x3f, sizeof(f));dfs(0, 0);cout << ans << endl;}return 0;
}

 

http://www.sczhlp.com/news/6762/

相关文章:

  • LDPC 原理以及译码原理(硬解和软解)
  • 3000 台 JuiceFS Windows 客户端性能评估
  • 保护器各功能模块
  • python 模块导入
  • 恋上Manim之Python安装
  • 【Java 温故而知新系列】基础知识-06 深入理解String类
  • C++ 中创建目录,判断目,文件
  • 哪些离线语音芯片适用于家电设备
  • 题2
  • 中国 Apache 项目 OpenRank 排行榜 Top 20:白鲸开源深度参与两大上榜项目
  • 粒子模拟效率提升50%!StokeMX 2.6安装激活教程全解锁
  • 电学笔记
  • DM数据库排查
  • 【IEEE出版】2025年国际膜计算会议暨人工智能与智能制造国际研讨会
  • 深入底层:如何优雅部署 SeaTunnel 分离集群到 Kubernetes
  • Atcoder 1200-1599 水题笔记
  • 【CAPL】常见报错和解决的tips
  • 使用trl-qlora微调qwen2.5
  • Java文件写入与编码、字节数组、字符集、字符编解码 一文打通!
  • 代码随想录算法训练营第一天(数组篇)|Leetcode704二分查找,Leetcode27移除元素,leetcode977有序数组的平方
  • vscode 基本配置
  • [快速阅读十一] 伊拉克团队的TAGC(低光增强效果)算法实现。
  • 基于GoogleNet深度学习网络和GEI步态能量提取的步态识别算法matlab仿真,数据库采用CASIA库
  • Linux网络:多路转接 epoll - 详解
  • LAS平台Vibe Data Processing:AI驱动的数据处理新范式
  • 坏的代码如何坏
  • 硅空位中心实现量子网络化的新突破
  • linux gpio-leds 作为硬盘指示灯
  • Goframe框架SetFileServerEnabled关闭静态服务不生效
  • 线段树算法:结合水果成篮的初步理解