5.4学习记录

今天的目标是复习+刷过往的提高课的DP题目:重点是数位DP,状态压缩DP,然后去做一些新的DP题目

然后明天的任务就是把DP的题目汇总,复习一些疑难的问题

方格取数:

题目背景

NOIP 2000 提高组 T4

题目描述

设有 N×N 的方格图 (N≤9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 0。如下图所示(见样例):

某人从图的左上角的 A 点出发,可以向下行走,也可以向右走,直到到达右下角的 B 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 0)。

此人从 A 点到 B 点共走两次,试找出 2 条这样的路径,使得取得的数之和为最大。

输入格式

输入的第一行为一个整数 N(表示 N×N 的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的 0 表示输入结束。

输出格式

只需输出一个整数,表示 2 条路径上取得的最大的和。

一次的走法:

fi1j1i2j2 表示两个路径,从(1,1)走到(i1,j1),从(1,1)走到(i2,j2)

什么时候两个路径会重合尼?:

显然当他们重合的时候有i1+j1==i2+j2这个是很显然的

所以我们可以用k表示总步数

fki1i2表示我们需要考虑的情况(其他状态下一步他们两个一定不会重合)

j1=k-i1 j2=k-i2

下面看我们的状态转移的方程:

四个推广的方向:

1下2下,1下2右,1右2右,1右2下

当然要判断一下这两个点走完过后不能够到同一个位置

如果重合的话,只加一次w(i,j)

如果没有重合的话,就要加上两次

代码:

#include<bits/stdc++.h>

using namespace std;

const int N=15;

int wNN;

int fN\*2NN;

int n;

int main(){

cin>>n;

while(1){

int x,y,c;

cin>>x>>y>>c;

if(x==0&&y==0&&c==0)break;

wxy=c;

}

for(int k=2;k<=2*n;k++){

for(int i=1;i<=n;i++){

for(int j=1;j<=n;j++){

int ii=k-i;

int jj=k-j;

if(ii<1||jj<1)continue;

if(ii>n||jj>n)continue;

int val=wiii;

if(i!=j)val+=wjjj;

fkij=max(fkij,fk-1i-1j-1+val);

fkij=max(fkij,fk-1ij-1+val);

fkij=max(fkij,fk-1ij+val);

fkij=max(fkij,fk-1i-1j+val);

}

}

}

cout<<f2\*nnn;

}

导弹防御系统:

为了对抗附近恶意国家的威胁,RR 国更新了他们的导弹防御系统。

一套防御系统的导弹拦截高度要么一直 严格单调 上升要么一直 严格单调 下降。

例如,一套系统先后拦截了高度为 33 和高度为 44 的两发导弹,那么接下来该系统就只能拦截高度大于 44 的导弹。

给定即将袭来的一系列导弹的高度,请你求出至少需要多少套防御系统,就可以将它们全部击落。

输入格式

输入包含多组测试用例。

对于每个测试用例,第一行包含整数 nn,表示来袭导弹数量。

第二行包含 nn 个不同的整数,表示每个导弹的高度。

当输入测试用例 n=0n=0 时,表示输入终止,且该用例无需处理。

输出格式

对于每个测试用例,输出一个占据一行的整数,表示所需的防御系统数量。

代码:

#include<bits/stdc++.h>

using namespace std;

const int N=55;

int aN;

int n;

int upN;

int downN;

int ans=0x3f3f3f3f;

void dfs(int pos,int pu,int pd){

if(pu+pd>=ans)return;

if(pos==n+1){

ans=min(ans,pu+pd);

return;

}

//放入上升序列

int k=0;

while(k<pu&&apos<=upk)k++;

int back=upk;

upk=apos;

if(k>=pu){

dfs(pos+1,pu+1,pd);

}else dfs(pos+1,pu,pd);

upk=back;

//放入下降序列

k=0;

while(k<pd&&apos>=downk)k++;

back=downk;

downk=apos;

if(k>=pd){

dfs(pos+1,pu,pd+1);

}else dfs(pos+1,pu,pd);

downk=back;

}

int main(){

while(scanf("%d",&n),n!=0){

for(int i=1;i<=n;i++)scanf("%d",&ai);

ans=0x3f3f3f3f;

memset(up,0,sizeof up);

memset(down,0,sizeof up);

dfs(1,0,0);

cout<<ans<<endl;

}

}

最长公共子序列:

(融合了LIS的nlogn的LCS)

题目描述

给出 1,2,...,n 的两个排列 P1​ 和 P2​ ,求它们的最长公共子序列。

输入格式

第一行是一个数 n。

接下来两行,每行为 n 个数,为自然数 1,2,...,n 的一个排列。

输出格式

一个数,即最长公共子序列的长度。

#include<iostream>

#include<cstdio>

using namespace std;

int a100001,b100001,map100001,f100001;

int main()

{

int n;

cin>>n;

for(int i=1;i<=n;i++){scanf("%d",&ai);mapa\[i]=i;}

for(int i=1;i<=n;i++){scanf("%d",&bi);fi=0x7fffffff;}

int len=0;

f0=0;

for(int i=1;i<=n;i++)

{

int l=0,r=len,mid;

int ans=0;

if(mapb\[i]>flen)f++len=mapb\[i];//f存的是长度为i的最后一个数的位置,如果和bi相等的ai,可以接在最长的len后面

else //不能接在最后面,就找到所有已有长度中最长的,满足fans<mapb\[i],以此去更新fans+1

{

while(l<=r)

{

mid=(l+r)/2;

if(fmid>mapb\[i]){//不能接在后面

r=mid-1;

}

else if(fmid<mapb\[i]){//可以接在fmid后面,去找找更长的

ans=mid;

l=mid+1;

}

}

fans+1=min(mapb\[i],fans+1);//可以接在ans后面,尝试去更新ans+1长度的最后一个值的更小的位置

}

}

cout<<len;

return 0;

}

最长上升子序列:

题目描述

这是一个简单的动规板子题。

给出一个由 n(n≤5000) 个不超过 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。

最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。

输入格式

第一行,一个整数 n,表示序列长度。

第二行有 n 个整数,表示这个序列。

#include <bits/stdc++.h>

using namespace std;

const int N=5010;

int n;

int aN;

int fN;

int main(){

cin>>n;

for(int i=1;i<=n;i++){

scanf("%d",&ai);

}

int len=0;

memset(f,0x3f,sizeof f);

f0=0;

for(int i=1;i<=n;i++){

int l=0,r=len;

int ans=0;

if(flen<ai)f++len=ai;//可以接在最后面

else{//尝试优化内部结构

while(l<=r){

int mid=(l+r)/2;

if(fmid>=ai){

r=mid-1;

}else if(fmid<ai){

ans=mid;

l=mid+1;

}

}

//可以接在ans的后面

fans+1=min(fans+1,ai);

}

}

cout<<len;

}

货币系统:

题目背景

NOIP2018 提高组 D1T2

题目描述

在网友的国度中共有 n 种不同面额的货币,第 i 种货币的面额为 ai,你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 n、面额数组为 a1..n 的货币系统记作 (n,a)。

在一个完善的货币系统中,每一个非负整数的金额 x 都应该可以被表示出,即对每一个非负整数 x,都存在 n 个非负整数 ti 满足 ai×ti 的和为 x。然而, 在网友的国度中,货币系统可能是不完善的,即可能存在金额 x 不能被该货币系统表示出。例如在货币系统 n=3, a=2,5,9 中,金额 1,3 就无法被表示出来。

两个货币系统 (n,a) 和 (m,b) 是等价的,当且仅当对于任意非负整数 x,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。

现在网友们打算简化一下货币系统。他们希望找到一个货币系统 (m,b),满足 (m,b) 与原来的货币系统 (n,a) 等价,且 m 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 m。

输入格式

输入文件的第一行包含一个整数 T,表示数据的组数。

接下来按照如下格式分别给出 T 组数据。 每组数据的第一行包含一个正整数 n。接下来一行包含 n 个由空格隔开的正整数 ai

输出格式

输出文件共有 T 行,对于每组数据,输出一行一个正整数,表示所有与 (n,a) 等价的货币系统 (m,b) 中,最小的 m。

代码:

#include <bits/stdc++.h>

using namespace std;

const int N=150;

int aN;

int f300000;

int n;

int t;

bool stN;

int main(){

scanf("%d",&t);

while(t--){

scanf("%d",&n);

for(int i=1;i<=n;i++)scanf("%d",&ai);

sort(a+1,a+1+n);

memset(f,0,sizeof f);

f0=1;

int res=0;

for(int i=1;i<=n;i++){

if(fa\[i])continue;

res++;

for(int j=ai;j<=an;j++){

fj=fj|fj-a\[i];

}

}

cout<<res<<endl;

}

}

有依赖的背包问题

有 N个物品和一个容量是 V 的背包。

物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。

如下图所示:

如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。

每件物品的编号是 ii,体积是 vivi,价值是 wiwi,依赖的父节点编号是 pipi。物品的下标范围是 1...N1...N。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 N,VN,V,用空格隔开,分别表示物品个数和背包容量。

接下来有 NN 行数据,每行数据表示一个物品。

第 ii 行有三个整数 vi,wi,pivi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。

如果 pi=−1pi=−1,表示根节点。 数据保证所有物品构成一棵树。

输出格式

输出一个整数,表示最大价值。

数据范围

1≤N,V≤1001≤N,V≤100

1≤vi,wi≤1001≤vi,wi≤100

父节点编号范围:

内部结点:1≤pi≤N1≤pi≤N;

根节点 pi=−1pi=−1

代码:

#include<bits/stdc++.h>

using namespace std;

const int N=110;

int fNN;

int vN,wN;

unordered_map<int,vector<int>>e;

int n,m;

void dfs(int u){

for(int to:eu){

dfs(to);

for(int j=m;j>=wu;j--){//循环 子树 使用体积,最少是wu

for(int k=wu;k<=j;k++)//循环 根 用的体积,最少是wu

fuj=max(fuj,fuk+ftoj-k);//fuk+ftoj-k

}

}

for(int j=wu;j<=m;j++)fuj+=vu;

}

int main(){

scanf("%d%d",&n,&m);

int root=0;

for(int i=1;i<=n;i++){

int a,b,c;

scanf("%d%d%d",&b,&a,&c);

vi=a;

wi=b;

if(c==-1)root=i;

else ec.push_back(i);

}

dfs(root);

cout<<frootm;

}

状态压缩dp基础:

二进制枚举

#include<bits/stdc++.h>

using namespace std;

int n;

void op(int x){

for(int i=0;i<n;i++){

if((x>>i)&1){

cout<<i+1<<' ';

}

}

}

int main(){

scanf("%d",&n);

for(int i=0;i<1<<n;i++){

op(i);

if(i+1!=1<<n)cout<<endl;

}

}

棋盘型状态压缩dp

蒙德里安的梦想:

求把 N×MN×M 的棋盘分割成若干个 1×21×2 的长方形,有多少种方案。

例如当 N=2,M=4N=2,M=4 时,共有 55 种方案。当 N=2,M=3N=2,M=3 时,共有 33 种方案。

如下图所示:

输入格式

输入包含多组测试用例。

每组测试用例占一行,包含两个整数 NN 和 MM。

当输入用例 N=0,M=0N=0,M=0 时,表示输入终止,且该用例无需处理。

输出格式

每个测试用例输出一个结果,每个结果占一行。

#include <bits/stdc++.h>

using namespace std;

// 定义常量 N 表示棋盘的列数相关的一个上限值,M 表示所有可能的状态数(2 的 N 次方)

const int N = 12, M = 1 << N;

// f 数组用于动态规划,第一维表示列数,第二维表示每一列的所有可能状态,存储方案数

long long fNM;

// st 数组用于存储每种状态是否有奇数个连续的 0,若有奇数个连续 0 则该状态无效,否则为 true

bool stM;

// state 是一个二维数组(这里用 vector<vector<int>> 实现),用于记录每一种状态下合法的前一列状态

vector<vector<int>> state(M);

int m, n; // m 表示棋盘的列数,n 表示棋盘的行数

int main() {

// 持续读入 n 和 m,只要 n 和 m 不同时为 0,就继续执行循环体

while (cin >> n >> m, n || m) {

// 遍历所有可能的状态(用二进制数表示,范围是 0 到 2 的 n 次方减 1)

for (int i = 0; i < (1 << n); i++) {

int cnt = 0; // 用于记录连续的 0 的个数

bool isValid = true; // 标记某种状态是否没有奇数个连续的 0,初始设为 true

// 遍历当前状态的每一位(对应棋盘的每一行),判断是否存在奇数个连续的 0

for (int j = 0; j < n; j++) {

// 判断状态 i 的二进制表示中第 j 位是否为 1

if ((i >> j) & 1) {

// 如果当前位为 1,检查前面连续的 0 的个数是否为奇数

if (cnt & 1) {

// 如果是奇数个连续的 0,则该状态不合法,标记为 false 并跳出循环

isValid = false;

break;

}

// 重置连续 0 的计数器,因为当前位为 1,新的连续 0 计数开始

cnt = 0;

}

else {

// 当前位为 0,连续 0 的计数器加 1

cnt++;

}

}

// 检查最后一段连续的 0 的个数是否为奇数,如果是则状态不合法

if (cnt & 1)

isValid = false;

// 将状态 i 是否有奇数个连续 0 的情况记录到 st 数组中

sti = isValid;

}

// 遍历第 i 列的所有状态(用二进制数表示,范围是 0 到 2 的 n 次方减 1)

for (int j = 0; j < (1 << n); j++) {

// 清空上一次操作遗留的状态,避免影响本次状态的判断

statej.clear();

// 遍历第 i - 1 列的所有状态(用二进制数表示,范围是 0 到 2 的 n 次方减 1)

for (int k = 0; k < (1 << n); k++) {

// 判断第 i 列的状态 j 和第 i - 1 列的状态 k 是否没有冲突(按位与为 0)

// 并且它们合并后的状态是合法的(stj \| k 为 true)

if ((j & k) == 0 && stj \| k)

// 如果满足条件,则将第 i - 1 列的状态 k 记录为第 i 列状态 j 的合法前一状态

statej.push_back(k);

}

}

// 动态规划部分开始,初始化 f 数组为 0

memset(f, 0, sizeof f);

// 初始化第 0 列,状态为 0 的方案数为 1

f00 = 1;

// 遍历每一列(从第 1 列到第 m 列)

for (int i = 1; i <= m; i++) {

// 遍历当前列(第 i 列)的所有状态 j(用二进制数表示,范围是 0 到 2 的 n 次方减 1)

for (int j = 0; j < (1 << n); j++) {

// 遍历第 i - 1 列中与当前状态 j 合法的所有状态 k

for (auto k : statej)

// 当前列状态 j 的方案数等于上一列(第 i - 1 列)状态 k 的方案数之和

fij += fi - 1k;

}

}

// 输出第 m 列,状态为 0 的方案数

cout << fm0 << endl;

}

return 0;

}

最短hamilton路径:

题目描述

给定一张 n 个点的带权无向图,点从 0∼n−1 标号,求起点 0 到终点 n−1 的最短 Hamilton 路径。

Hamilton 路径的定义是从 0 到 n−1 不重不漏地经过每个点恰好一次。

输入格式

第一行输入整数 n。

接下来 n 行每行 n 个整数,其中第 i 行第 j 个整数表示点 i−1 到 j−1 的距离(记为 ai−1,j−1)。

对于任意的 x,y,z,数据保证 ax,x=0,ax,y=ay,x 并且 ax,y+ay,z≥ax,z

输出格式

输出一个整数,表示最短 Hamilton 路径的长度。

代码:

#include <bits/stdc++.h>

using namespace std;

const int N=21;

int gNN;

int f1\<\N;

int n;

int main(){

cin>>n;

for(int i=0;i<n;i++)

for(int j=0;j<n;j++)

cin>>gij;

memset(f,0x3f,sizeof f);

f10=0;

int m=1<<n;

for(int i=0;i<m;i++){//遍历所有集合

for(int j=0;j<n;j++){//从集合中的j

if((1<<j)&i){//j在i中

for(int k=0;k<n;k++){//走到k

if(k!=j&&(1<<k)&i){//j不等于k,并且k在i中

fij=min(fij,fi-(1\<\k+gkj);//从k转移过来

}

}

}

}

}

cout<<fm-1n-1;

return 0;

}

玉米田:

农夫约翰的土地由 M×NM×N 个小方格组成,现在他要在土地里种植玉米。

非常遗憾,部分土地是不育的,无法种植。

而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。

现在给定土地的大小,请你求出共有多少种种植方法。

土地上什么都不种也算一种方法。

输入格式

第 11 行包含两个整数 MM 和 NN。

第 2..M+12..M+1 行:每行包含 NN 个整数 00 或 11,用来描述整个土地的状况,11 表示该块土地肥沃,00 表示该块土地不育。

输出格式

输出总种植方法对 108108 取模后的值。

代码:

#include<bits/stdc++.h>

using namespace std;

const int mod=1e9;

const int N=15;

int fN1\<\;

int n,m;

vector<int> t;

unordered_map<int,vector<int>> e;

int gN;

int op(int x){

return ((x+mod)%mod+mod)%mod;

}

bool check(int x){//连续的两个不能都是1

if(x&(x<<1))return false;

return true;

}

int main(){

cin>>n>>m;

for(int i=0;i<(1<<m);i++){

if(check(i))t.push_back(i);

}

for(int i:t){

for(int j:t){//和j匹配的i

if((i&j)==0)ei.push_back(j);

}

}

for(int i=1;i<=n;i++){

for(int j=0;j<m;j++){

int tmp;

cin>>tmp;

if(tmp==0){

gi+=(1<<j);

}

}

}//读入状态

f00=1;

for(int i=1;i<=n+1;i++){

for(int j:t){

if(j&gi)continue;//没有种在坏地上

for(int k:ej){

if(k&gi-1)continue;//上一行也合法

fij=op(fij+fi-1k);

}

}

}

cout<<fn+10;

}

炮兵阵地:

题目描述

司令部的将军们打算在 N ×M 的网格地图上部署他们的炮兵部队。

一个 N ×M 的地图由 NM 列组成,地图的每一格可能是山地(用 H 表示),也可能是平原(用 P 表示),如下图。

在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。

现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

输入格式

第一行包含两个由空格分割开的正整数,分别表示 NM

接下来的 N 行,每一行含有连续的 M 个字符,按顺序表示地图中每一行的数据。

输出格式

一行一个整数,表示最多能摆放的炮兵部队的数量。

代码:

#include <bits/stdc++.h>

using namespace std;

const int N=110,M=(1<<10);

int sN={0},cntM;

int n,m;

int stateM;

int f2MM={0};

bool check (int st){

for(int i=0;i<10;i++){

if(((st>>i)&1)&&(((st>>(i+1))&1)||((st>>(i+2))&1)))//3个连续的地方只有1个1

return false;

}

return true;

}

int count(int st){

int res=0;

for(int i=0;i<11;i++){

if((st>>i)&1)res++;

}

return res;

}

int main(){

cin>>n>>m;

for(int i=1;i<=n;i++){

for(int j=0;j<m;j++){

char tmp;

cin>>tmp;

if(tmp=='H')

si+=(1<<j);

}

}

int cn=0;

int t=1<<m;

for(int i=0;i<t;i++){//遍历所有的状态

if(check(i)){//i状态合法

statecn++=i;//将其加入到state种

cnti=count(i);//同时计算状态中的炮兵数量

}

}

for(int i=1;i<=n+2;i++)//遍历到n+2,最后的答案是n+2行的状态是0

for(int j=0;j<cn;j++)//第i-1行的状态

for(int k=0;k<cn;k++)//第i行的状态

for(int u=0;u<cn;u++){//i-2行的状态

int a=statek,b=statej,c=stateu;

if((a&c)||(b&c)||(a&b))continue;//3行里面不能有炮兵重叠

if(si&a||si-1&b)continue;//山地冲突

if(i>=2&&si-2&c)continue;//山地冲突

fi\&1jk=max(fi\&1jk,fi-1\&1uj+cnta);

}

cout<<fn+2\&100;//state0对应状态0000000000

}

愤怒的小鸟:

题目背景

NOIP2016 提高组 D2T3

题目描述

Kiana 最近沉迷于一款神奇的游戏无法自拔。

简单来说,这款游戏是在一个平面上进行的。

有一架弹弓位于 (0,0) 处,每次 Kiana 可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如 y =ax 2+bx 的曲线,其中 a ,b 是 Kiana 指定的参数,且必须满足 a <0,a ,b 都是实数。

当小鸟落回地面(即 x 轴)时,它就会瞬间消失。

在游戏的某个关卡里,平面的第一象限中有 n 只绿色的小猪,其中第 i 只小猪所在的坐标为 (x i ,y i )。

如果某只小鸟的飞行轨迹经过了 (x i ,y i ),那么第 i 只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;

如果一只小鸟的飞行轨迹没有经过 (x i ,y i ),那么这只小鸟飞行的全过程就不会对第 i 只小猪产生任何影响。

例如,若两只小猪分别位于 (1,3) 和 (3,3),Kiana 可以选择发射一只飞行轨迹为 y =−x 2+4x 的小鸟,这样两只小猪就会被这只小鸟一起消灭。

而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。

这款神奇游戏的每个关卡对 Kiana 来说都很难,所以 Kiana 还输入了一些神秘的指令,使得自己能更轻松地完成这个游戏。这些指令将在【输入格式】中详述。

假设这款游戏一共有 T 个关卡,现在 Kiana 想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。由于她不会算,所以希望由你告诉她。

输入格式

第一行包含一个正整数 T ,表示游戏的关卡总数。

下面依次输入这 T 个关卡的信息。每个关卡第一行包含两个非负整数 n ,m ,分别表示该关卡中的小猪数量和 Kiana 输入的神秘指令类型。接下来的 n 行中,第 i 行包含两个正实数 x i ,y i ,表示第 i 只小猪坐标为 (x i ,y i )。数据保证同一个关卡中不存在两只坐标完全相同的小猪。

如果 m =0,表示 Kiana 输入了一个没有任何作用的指令。

如果 m =1,则这个关卡将会满足:至多用 ⌈n /3+1⌉ 只小鸟即可消灭所有小猪。

如果 m =2,则这个关卡将会满足:一定存在一种最优解,其中有一只小鸟消灭了至少 ⌊n /3⌋ 只小猪。

保证 1≤n ≤18,0≤m ≤2,0<x i ,y i <10,输入中的实数均保留到小数点后两位。

上文中,符号 ⌈c ⌉ 和 ⌊c ⌋ 分别表示对 c 向上取整和向下取整,例如:⌈2.1⌉=⌈2.9⌉=⌈3.0⌉=⌊3.0⌋=⌊3.1⌋=⌊3.9⌋=3。

输出格式

对每个关卡依次输出一行答案。

输出的每一行包含一个正整数,表示相应的关卡中,消灭所有小猪最少需要的小鸟数量。

代码:

#include<bits/stdc++.h>

using namespace std;

typedef pair<double,double> pdd;

const int N=20;

bool same(double x,double y){

if(fabs(x-y)<1e-6)return 1;

return 0;

}

int t,n,m;

pdd aN;

int fNN;

int g1\<\;

int main(){

scanf("%d",&t);

while(t--){

cin>>n>>m;

for(int i=0;i<n;i++){

cin>>ai.first>>ai.second;

}

memset(f,0,sizeof f);

for(int i=0;i<n;i++){

fii=1<<i;

for(int j=0;j<n;j++){

if(i==j)continue;

double x1=ai.first,y1=ai.second;

double x2=aj.first,y2=aj.second;

if(same(x1,x2))continue;

double aa=(y1/x1-y2/x2)/(x1-x2);

if(aa>0)continue;

double bb=y1/x1-aa*x1;

int st=0;

for(int k=0;k<n;k++){

double x=ak.first,y=ak.second;

if(same(aa*x*x+bb*x,y)){

st+=1<<k;

}

}

fij=st;//表示从依据i和j建立的抛物线覆盖的点集合

}

}

memset(g,0x3f,sizeof g);

g0=0;

for(int i=0;i<1<<n;i++){

int x=0;

for(int j=0;j<n;j++){//找到没有被覆盖的点

if(i>>j&1){

continue;

}else {

x=j;

break;

}

}

for(int j=0;j<n;j++){

gi\|f\[xj]=min(gi\|f\[xj],gi+1);

}

}

cout<<g(1\<\<<endl;

}

}

明天从区间Dp开始看

相关推荐
WMX1012几秒前
Unity-shader学习记录
学习·unity·游戏引擎
一只老丸几秒前
HOT100题打卡第27天——动态规划(hard)
算法·动态规划
羑悻的小杀马特几秒前
【动态规划篇】正则表达式与通配符:开启代码匹配的赛博奇幻之旅
c++·算法·leetcode·正则表达式
吴可可1231 分钟前
SolidWorks二次开发实战应用
算法
Mister西泽5 分钟前
线性代数-学习日记
学习
sakiko_10 分钟前
Swift学习笔记35-本地化
笔记·学习·swift
春日见12 分钟前
5分钟入门强化学习之蒙特卡洛(MC)算法与实现
运维·服务器·人工智能·深度学习·算法·机器学习
x_xbx14 分钟前
LeetCode:581. 最短无序连续子数组
算法·leetcode·排序算法
星幻元宇VR20 分钟前
VR心理骑行设备:心理健康教育的新型互动体验
科技·学习·安全·vr
代码中介商20 分钟前
排序算法完全指南(八):归并排序深度详解
数据结构·算法·排序算法