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

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 条路径上取得的最大的和。


一次的走法:

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

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

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

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

f[k][i1][i2]表示我们需要考虑的情况(其他状态下一步他们两个一定不会重合)

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 w[N][N];

int f[N*2][N][N];

int n;

int main(){

    cin>>n;

    while(1){

        int x,y,c;

        cin>>x>>y>>c;

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

        w[x][y]=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=w[i][ii];

                if(i!=j)val+=w[j][jj];

                f[k][i][j]=max(f[k][i][j],f[k-1][i-1][j-1]+val);

                f[k][i][j]=max(f[k][i][j],f[k-1][i][j-1]+val);

                f[k][i][j]=max(f[k][i][j],f[k-1][i][j]+val);

                f[k][i][j]=max(f[k][i][j],f[k-1][i-1][j]+val);

            }

        }   

    }

    cout<<f[2*n][n][n];

}

导弹防御系统:

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

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

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

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

输入格式

输入包含多组测试用例。

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

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

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

输出格式

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

代码:

#include<bits/stdc++.h>

using namespace std;

const int N=55;

int a[N];

int n;

int up[N];

int down[N];

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&&a[pos]<=up[k])k++;

    int back=up[k];

    up[k]=a[pos];

    if(k>=pu){

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

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

    up[k]=back;

    

    //放入下降序列

    k=0;

    while(k<pd&&a[pos]>=down[k])k++;

    back=down[k];

    down[k]=a[pos];

    if(k>=pd){

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

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

    down[k]=back;

}

int main(){

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

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

        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 a[100001],b[100001],map[100001],f[100001];

int main()

{

int n;

cin>>n;

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

for(int i=1;i<=n;i++){scanf("%d",&b[i]);f[i]=0x7fffffff;}

int len=0;

f[0]=0;

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

{

int l=0,r=len,mid;

        int ans=0;

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

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

{

while(l<=r)

{

    mid=(l+r)/2;

    if(f[mid]>map[b[i]]){//不能接在后面

                r=mid-1;

            }

else if(f[mid]<map[b[i]]){//可以接在f[mid]后面,去找找更长的

                ans=mid;

                l=mid+1;

            }

}

f[ans+1]=min(map[b[i]],f[ans+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 a[N];

int f[N];

int main(){

    cin>>n;

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

        scanf("%d",&a[i]);

    }

    int len=0;

    memset(f,0x3f,sizeof f);

    f[0]=0;

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

        int l=0,r=len;

        int ans=0;

        if(f[len]<a[i])f[++len]=a[i];//可以接在最后面

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

            while(l<=r){

                int mid=(l+r)/2;

                if(f[mid]>=a[i]){

                    r=mid-1;

                }else if(f[mid]<a[i]){

                    ans=mid;

                    l=mid+1;

                }

            }    

            //可以接在ans的后面

            f[ans+1]=min(f[ans+1],a[i]);

        }

    }

    

    cout<<len;

}

货币系统:


题目背景

NOIP2018 提高组 D1T2

题目描述

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

在一个完善的货币系统中,每一个非负整数的金额 x 都应该可以被表示出,即对每一个非负整数 x,都存在 n 个非负整数 t[i] 满足 a[i]×t[i] 的和为 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 个由空格隔开的正整数 a[i]。

输出格式

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

代码:
#include <bits/stdc++.h>

using namespace std;

const int N=150;

int a[N];

int f[300000];

int n;

int t;

bool st[N];

int main(){

    scanf("%d",&t);

    while(t--){

        scanf("%d",&n);

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

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

        memset(f,0,sizeof f);

        f[0]=1;

        int res=0;

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

            if(f[a[i]])continue;

            res++;

            for(int j=a[i];j<=a[n];j++){

                f[j]=f[j]|f[j-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 f[N][N];

int v[N],w[N];

unordered_map<int,vector<int>>e;

int n,m;

void dfs(int u){

    for(int to:e[u]){

        dfs(to);

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

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

                f[u][j]=max(f[u][j],f[u][k]+f[to][j-k]);//f[u][k]+f[to][j-k]

        }

    }

    

    for(int j=w[u];j<=m;j++)f[u][j]+=v[u];

    

}

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);

        v[i]=a;

        w[i]=b;

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

        else e[c].push_back(i);

    }

    dfs(root);

    cout<<f[root][m];

}

状态压缩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 f[N][M];  

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

bool st[M];  

// 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 数组中

            st[i] = isValid;

        }

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

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

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

            state[j].clear();

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

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

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

                // 并且它们合并后的状态是合法的(st[j | k] 为 true)

                if ((j & k) == 0 && st[j | k])

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

                    state[j].push_back(k);  

            }

        }

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

        memset(f, 0, sizeof f);  

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

        f[0][0] = 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 : state[j])  

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

                    f[i][j] += f[i - 1][k];    

            }

        }

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

        cout << f[m][0] << 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 的距离(记为 a[i−1,j−1])。

对于任意的 x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]≥a[x,z]。

输出格式

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

代码:
#include <bits/stdc++.h>

using namespace std;

const int N=21;

int g[N][N];

int f[1<<N][N];

int n;

int main(){

    cin>>n;

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

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

            cin>>g[i][j];

    memset(f,0x3f,sizeof f);

    f[1][0]=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中

                        f[i][j]=min(f[i][j],f[i-(1<<j)][k]+g[k][j]);//从k转移过来

                    }

                }

            }

        }

    }

    cout<<f[m-1][n-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 f[N][1<<N];

int n,m;

vector<int> t;

unordered_map<int,vector<int>> e;

int g[N];

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)e[i].push_back(j);

        }

    }

    

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

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

            int tmp;

            cin>>tmp;

            if(tmp==0){

                g[i]+=(1<<j);

            }

        }

    }//读入状态

    

    f[0][0]=1;

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

        for(int j:t){

            if(j&g[i])continue;//没有种在坏地上

            for(int k:e[j]){

                if(k&g[i-1])continue;//上一行也合法

                f[i][j]=op(f[i][j]+f[i-1][k]);

            }

        }

    }

    cout<<f[n+1][0];

}

炮兵阵地:


题目描述

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

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

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

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

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

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

输入格式

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

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

输出格式

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

代码:
#include <bits/stdc++.h>

using namespace std;

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

int s[N]={0},cnt[M];

int n,m;

int state[M];

int f[2][M][M]={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')

                s[i]+=(1<<j);

        }

    }

    

    int cn=0;

    int t=1<<m;

    

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

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

            state[cn++]=i;//将其加入到state种

            cnt[i]=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=state[k],b=state[j],c=state[u];

                    

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

                    if(s[i]&a||s[i-1]&b)continue;//山地冲突

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

                    f[i&1][j][k]=max(f[i&1][j][k],f[i-1&1][u][j]+cnt[a]);

                }

    cout<<f[n+2&1][0][0];//state[0]对应状态0000000000

}

愤怒的小鸟:


题目背景

NOIP2016 提高组 D2T3

题目描述

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

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

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

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

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

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

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

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

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

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

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

输入格式

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

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

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

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

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

保证 1≤n≤180≤m≤20<xi,yi<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 a[N];

int f[N][N];

int g[1<<N];

int main(){

    scanf("%d",&t);

    while(t--){

        cin>>n>>m;

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

            cin>>a[i].first>>a[i].second;

        }

        memset(f,0,sizeof f);

        

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

            f[i][i]=1<<i;

            

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

                if(i==j)continue;

                

                double x1=a[i].first,y1=a[i].second;

                double x2=a[j].first,y2=a[j].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=a[k].first,y=a[k].second;

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

                        st+=1<<k;

                    }

                }

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

            }

        }

    

        memset(g,0x3f,sizeof g);

        g[0]=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++){

                g[i|f[x][j]]=min(g[i|f[x][j]],g[i]+1);

            }

        }

        cout<<g[(1<<n)-1]<<endl;

        

    }

    

}

明天从区间Dp开始看

相关文章:

5.4学习记录

今天的目标是复习刷过往的提高课的DP题目&#xff1a;重点是数位DP&#xff0c;状态压缩DP&#xff0c;然后去做一些新的DP题目 然后明天的任务就是把DP的题目汇总&#xff0c;复习一些疑难的问题 方格取数&#xff1a; 题目背景 NOIP 2000 提高组 T4 题目描述 设有 NN 的方…...

Hadoop 1.x设计理念解析

一、背景 有人可能会好奇&#xff0c;为什么要学一个二十年前的东西呢&#xff1f; Hadoop 1.x虽然是二十年前的&#xff0c;但hadoop生态系统中的一些组件如今还在广泛使用&#xff0c;如hdfs和yarn&#xff0c;当今流行spark和flink都依赖这些组件 通过学习它们的历史设计…...

缓存与数据库的高效读写流程解析

目录 前言1 读取数据的流程1.1 检查缓存是否命中1.2 从数据库读取数据1.3 更新缓存1.4 返回数据 2 写入数据的流程2.1 更新数据库2.2 更新或删除缓存2.3 缓存失效 3 缓存与数据库的一致性问题3.1 写穿&#xff08;Write-through&#xff09;策略3.2 写回&#xff08;Write-back…...

Linux中的粘滞位和开发工具和文本编辑器vim

1.粘滞位的使用的背景&#xff1a; 当几个普通用户需要文件共享操作时&#xff0c;他们就需要在同一个目录下进行操作&#xff0c;那么就诞生一个问题&#xff0c;由谁来创建这个公共的目录文件&#xff1f;假设是由其中的一个普通用户来创建一个默认的目录文件&#xff0c;这就…...

冯诺依曼结构与哈佛架构深度解析

一、冯诺依曼结构&#xff08;Von Neumann Architecture&#xff09; 1.1 核心定义 由约翰冯诺依曼提出&#xff0c;程序指令与数据共享同一存储空间和总线&#xff0c;通过分时复用实现存取。 存储器总带宽 指令带宽 数据带宽 即&#xff1a;B_mem f_clk W_data f_…...

如何提升个人情商?

引言 提升个人情商&#xff08;EQ&#xff09;是一个持续的自我修炼过程&#xff0c;涉及自我认知、情绪管理、人际沟通等多个方面。以下是一些具体且可实践的方法&#xff0c;帮助你逐步提升情商&#xff1a; 一、提升自我觉察能力 1. 记录情绪日记 每天回顾自己的情绪…...

JSON Web Token 默认密钥 身份验证安全性分析 dubbo-admin JWT硬编码身份验证绕过

引言 在web开发中&#xff0c;对于用户认证的问题&#xff0c;有很多的解决方案。其中传统的认证方式&#xff1a;基于session的用户身份验证便是可采用的一种。 基于session的用户身份验证验证过程&#xff1a; 用户在用进行验证之后&#xff0c;服务器保存用户信息返回sess…...

K230的ISP(图像信号处理器)通常支持多通道输出,常见配置为3个独立通道

也就是一个摄像头可以拍摄三种配置的图片&#xff0c;这样就可以调用三种&#xff1a; img_try sensor.snapshot(chnCAM_CHN_ID_0) img_try2 sensor.snapshot(chnCAM_CHN_ID_1) img_try3 sensor.snapshot(chnCAM_CHN_ID_2) 这样可以一图多用 eg&#xff1a; # 初始化并配…...

工程师 - 小米汽车尾部主动扩散器

关于小米SU7 Ultra的主动尾部扩散器&#xff0c;其设计初衷是为了平衡日常驾驶的节能需求与运动驾驶的操控性能。这一装置位于车辆尾部下方&#xff0c;具备自动调节功能&#xff0c;能够根据车速在0和32之间切换&#xff0c;同时也支持手动调整。 32度打开状态&#xff1a; 0度…...

Linux watch 命令使用详解

简介 watch 命令会以固定间隔&#xff08;默认每 2 秒&#xff09;重复运行给定命令&#xff0c;并在终端上显示其输出。它非常适合监控不断变化的输出&#xff0c;例如磁盘使用情况、内存使用情况、文件更改、服务状态等。 基础语法 watch [options] command常用选项 -n, -…...

RabbitMQ-基础

RabbitMQ-基础 文章目录 RabbitMQ-基础1.同步调用2.异步调用3.技术选型4.安装RabbitMQ(官方网址)https://www.rabbitmq.com/5.快速入门5.1收发消息5.1.1交换机5.1.2队列5.1.3绑定关系5.1.4发送消息 5.2数据隔离5.2.1用户管理5.2.2virtual host 6.Java客户端操作RabbitMQ6.1快速…...

第九周作业

安全专题笔记 1、文件上传 (1) 服务端白名单绕过 %00截断绕过要求虚拟机中搭建实验环境&#xff0c;分别实现GET、POST方法的绕过 前提条件&#xff1a; 1 php的版本需要在5.4以下 2 magic_quotes_gpc需要设置为off 启动phpstudy&#xff0c;前往php-ini将magic_quotes_gpc…...

AtCoder Beginner Contest 404 C-G(无F)题解

C. Cycle Graph? 题意 给你一个 N N N 个顶点 M M M 条边的简单&#xff08;无重边、自环&#xff09;无向图&#xff0c;第 i i i 条边连接节点 A i A_i Ai​ 和 B i B_i Bi​&#xff0c;判断这个图是不是一个环。 思路 首先一个图是环&#xff0c;要满足点数等于边…...

Python----机器学习(模型评估:准确率、损失函数值、精确度、召回率、F1分数、混淆矩阵、ROC曲线和AUC值、Top-k精度)

一、模型评估 1. 准确率&#xff08;Accuracy&#xff09;&#xff1a;这是最基本的评估指标之一&#xff0c;表示模型在测试集上正确 分类样本的比例。对于分类任务而言&#xff0c;准确率是衡量模型性能的直观标准。 2. 损失函数值&#xff08;Loss&#xff09;&#xff1…...

开上“Python跑的车”——自动驾驶数据可视化的落地之道

开上“Python跑的车”——自动驾驶数据可视化的落地之道 一、自动驾驶离不开“看得见”的智能 在智能汽车时代,自动驾驶已然不是“炫技”标签,而是一场技术实力的全面拉锯战。而在这场战役中,有一个极其关键但常被忽略的领域,叫做: 数据可视化(Data Visualization)。 为…...

Linux内核gcov修改为模块

Linux内核gcov修改为模块 Gcov 是 GNU 项目开发的代码覆盖率分析工具&#xff0c;与 GCC 编译器深度集成&#xff0c;用于统计程序运行时代码的执行情况&#xff0c;帮助开发者评估测试用例的完整性和代码质量。 Gcov工作原理 ​1. 编译插桩 编译时需添加 -fprofile-arcs -…...

【安装配置教程】linux部署AList记录

之前朋友安利给自己AList&#xff0c;这个工具可以很方便的管理个人的网盘内容&#xff0c;可以随时上传下载拉取&#xff0c;于是心血来潮自己部署并记录一下。 一、拉取下载脚本 在AList官网&#xff0c;找到安装下面的一键脚本 curl -fsSL "https://alist.nn.ci/v3.sh…...

题解:AT_abc245_e [ABC245E] Wrapping Chocolate

我绝对不会告诉你我打比赛时没做出来这道题。 题目简化&#xff1a;给定每个巧克力和盒子的长宽&#xff0c;已知每个盒子只能放一块巧克力&#xff0c;并且必须保证巧克力能放下&#xff0c;求是否所有巧克力都能放入。 思路&#xff1a;贪心、二分、排序、STL。 首先看到这…...

Linux 入门:操作系统进程详解(上)

目录 一.冯诺依曼体系结构 一&#xff09;. 软件运行前为什么要先加载&#xff1f;程序运行之前在哪里&#xff1f; 二&#xff09;.理解数据流动 二.操作系统OS(Operator System) 一&#xff09;.概念 二&#xff09;.设计OS的目的 三&#xff09;.如何理解操作系统…...

5.7/Q1,GBD数据库最新文章解读

文章题目&#xff1a;Global, regional, and national burden and trends of rheumatoid arthritis among the elderly population: an analysis based on the 2021 Global Burden of Disease study DOI&#xff1a;10.3389/fimmu.2025.1547763 中文标题&#xff1a;全球、区域…...

[pdf,epub]292页《分析模式》漫谈合集01-59提供下载

《分析模式》漫谈合集01-59的pdf、epub文件提供下载&#xff0c;地址&#xff1a; umlchina.com/url/ap.html&#xff0c;或查看本账号的CSDN资源。 已排版成适合手机阅读&#xff0c;pdf的排版更好一些。...

Spring MVC的工作流程, DispatcherServlet 的工作流程

Spring MVC 是一种基于Java的模型-视图-控制器&#xff08;MVC&#xff09;Web框架&#xff0c;它通过清晰的角色划分简化了Web应用开发。下面是Spring MVC的工作流程以及DispatcherServlet的具体工作流程。 Spring MVC 工作流程 请求到达&#xff1a;客户端发起一个HTTP请求…...

【Godot】使用 Shader 实现可配置圆角效果

文章目录 效果预览实现原理完整Shader代码关键参数详解1. 半径参数(radius)2. 角开关参数(hide_*)数学原理圆形区域判定公式坐标映射性能优化使用示例编辑器操作代码控制进阶技巧1. 添加抗锯齿2. 外发光效果3. 动画效果常见问题解决方案问题1:圆角边缘锯齿问题2:圆形变形…...

【翻译、转载】MCP 提示 (Prompts)

原文地址&#xff1a;https://modelcontextprotocol.io/docs/concepts/prompts#python 提示 (Prompts) 创建可重用的提示模板和工作流 提示 (Prompts) 使服务器能够定义可重用的提示模板和工作流&#xff0c;客户端可以轻松地将其呈现给用户和 LLM。它们提供了一种强大的方式来…...

论快乐的学习和学习的快乐

目录 一、背景二、过程1.快乐的学习&#xff1a;理念与实践快乐学习的理念溯源快乐学习在教育实践中的体现 2.学习的快乐&#xff1a;内涵与价值学习的快乐的多维内涵学习的快乐对个人成长的价值 3.快乐的学习与学习的快乐的相互关系快乐的学习是学习快乐的重要前提学习的快乐是…...

Git 命令

参考文献&#xff1a; Git 教程 | 菜鸟教程Git 使用教程&#xff1a;最详细、最正宗手把手教学&#xff08;万字长文&#xff09;git忽略某个目录或文件不上传 文章目录 工作原理基本命令配置使用 其他命令日志分支回退标签 忽略指定文件远程仓库 工作原理 Git 是由 Linus To…...

365打卡第R6周: LSTM实现糖尿病探索与预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 &#x1f3e1; 我的环境&#xff1a; 语言环境&#xff1a;Python3.10 编译器&#xff1a;Jupyter Lab 深度学习环境&#xff1a;torch2.5.1 torchvision0…...

新能源实验室电磁兼容设计优化方案论述

摘要&#xff1a;本文旨在进行新能源核心部件/系统测试实验室电磁兼容情况设计及优化方案进行论述&#xff0c;通过系统化梳理实验室的主流设备仪器&#xff0c;试验搭建典型方案。识别不同设备的电磁兼容现状&#xff0c;实验室基于设备布局常见设计方案不足点&#xff0c;故障…...

计算机图形学中的深度学习

文章目录 零、前言0.课程考核1.课程大纲2.前置知识3.教材4.课程大纲5.相关课程 Relevant Courses 一、计算机图形学1.本章学习目标2.图形学的应用3.SIG Graph papers 二、基本图形生成算法1.本章学习目标2.图形API3.OpenGL(1)什么是OpenGL(2)OpenGL 的基本组件&#xff1a;顶点…...

RockyLinux9.3-24小时制

在 RockyLinux 9.3 中&#xff0c;默认时间格式为 12 小时制&#xff0c;调整为 24 小时制 案例一&#xff1a;在 RockyLinux 9.3 中&#xff0c;默认时间格式为 12 小时制&#xff0c;调整为 24 小时制案例二&#xff1a;时间显示英文调整为中文endl 案例一&#xff1a;在 Roc…...

25.2linux中外置RTC芯片的PCF8563实验(测试)_csdn

1、硬件原理图分析 知道了这些引脚我们还是按照老习惯&#xff01; 配置镜像和设备树文件&#xff01; 2、修改设备树 2.1、添加或者查找 PCF8563 所使用的 IO 的 pinmux 配置 打开stm32mp15-pincrtl.dtsi 文件&#xff0c;查找节点I2C4: 也就是中断引脚并不需要配置pinctrl…...

高性能 WEB 服务器 Nginx:多虚拟主机实现!

Nginx 配置多虚拟主机实现 多虚拟主机是指在一台 Nginx 服务器上配置多个网站 在 Nginx 中&#xff0c;多虚拟主机有三种实现方式&#xff1a; 基于IP地址实现多虚拟主机 基于端口号实现多虚拟主机 基于域名实现多虚拟主机 1 基于域名实现多虚拟主机 在 Nginx 中配置多个…...

C++ 的类型排序

0.前言 在 C 中&#xff0c;我编写了一个 tuple-like 模板&#xff0c;这个模板能容纳任意多且可重复的类型&#xff1a; template<typename... Ts> struct TypeList {};// usage: using List1 TypeList<int, double, char, double>; using List2 TypeList<…...

[计算机网络]拓扑结构

拓扑结构一般会在计网教材或课程的第一章计网的分类那里接触到&#xff0c;但实际上计网的拓扑结构并不只是第一章提到的总线型、星型、树型、网状、混合型那几种类型那么简单&#xff0c;学完了后面的数链层以后对拓扑结构会有新的体会&#xff0c;所以特别单独总结成一篇博客…...

C#方法返回值全解析:从基础语法到实战技巧

摘要&#xff1a;方法返回值是C#编程的核心概念之一。本文将带你彻底掌握返回值声明、void方法特性&#xff0c;以及如何通过返回值实现优雅的流程控制&#xff08;文末附完整示例代码&#xff09;。 返回值的基础法则 类型声明原则 有返回值&#xff1a;必须在方法名前声明…...

修复笔记:SkyReels-V2 项目中的 torch.cuda.amp.autocast 警告和错误

#工作记录 一、问题描述 在运行项目时&#xff0c;出现以下警告和错误&#xff1a; FutureWarning: torch.cuda.amp.autocast(args...) is deprecated. Please use torch.amp.autocast(cuda, args...) instead.with torch.cuda.amp.autocast(dtypepipe.transformer.dtype), …...

【TF-BERT】基于张量的融合BERT多模态情感分析

不足&#xff1a;1. 传统跨模态transformer只能处理2种模态&#xff0c;所以现有方法需要分阶段融合3模态&#xff0c;引发信息丢失。2. 直接拼接多模态特征到BERT中&#xff0c;缺乏动态互补机制&#xff0c;无法有效整合非文本模态信息 改进方法&#xff1a;1. 基于张量的跨模…...

SONiC-OTN代码详解(具体内容待续)

SONiC-OTN代码详解 &#xff08;具体内容待续&#xff09; 基于AI的源代码解析工具的产生使得代码阅读和解析变得越来越高效和简洁&#xff0c;计划通过这样的工具对SONiC在OTN领域的应用做一个全自动的解析&#xff0c;大部分内容会基于AI工具的自动解析结果。这样做的目的是…...

牛客周赛90 C题- Tk的构造数组 题解

原题链接 https://ac.nowcoder.com/acm/contest/107500/C 题目描述 解题思路 数组a是不可以动的&#xff0c;所以我们可以把a[i]*b[i]*i分成两组&#xff0c;分别为a[i]*i以及b[i] 然后策略就很明显了&#xff0c;让更大的b[i]匹配更大的a[i]*i 详细实现见代码。 代码&am…...

[ML]通过50个Python案例了解深度学习和神经网络

通过50个Python案例了解深度学习和神经网络 摘要:机器学习 (Machine Learning, ML)、深度学习 (Deep Learning, DL) 和神经网络 (Neural Networks, NN) 是人工智能领域的核心技术。Python 是学习和实践这些技术的首选语言,因为它提供了丰富的库(如 scikit-learn、Te…...

vue3 - keepAlive缓存组件

在Vue 3中&#xff0c;<keep-alive>组件用于缓存动态组件或路由组件的状态&#xff0c;避免重复渲染&#xff0c;提升性能。 我们新建两个组件&#xff0c;在每一个组件里面写一个input&#xff0c;在默认情况下当组件切换的时候&#xff0c;数据会被清空&#xff0c;但…...

自由学习记录(58)

Why you were able to complete the SpringBoot MyBatisPlus task smoothly: Clear logic flow: Database → Entity → Service → Controller → API → JSON response. Errors are explicit, results are verifiable — you know what’s broken and what’s fixed. Sta…...

短信侠 - 自建手机短信转发到电脑上并无感识别复制验证码,和找手机输验证码说再见!

自建手机短信转发到电脑上并无感识别复制验证码 一、前言 项目开发语言&#xff1a;本项目使用PythonRedisC#开发 你是否也遇到过这样的场景&#xff1a; 正在电脑上操作某个网站&#xff0c;需要输入短信验证码手机不在身边&#xff0c;或者在打字时来回切换设备很麻烦验证码…...

课程10. 聚类问题

课程10. 聚类问题 聚类此类表述的难点K 均值法让我们推广到几个集群的情况如果我们选择其他起始近似值会怎样&#xff1f; 结论在 sklearn 中的实现 如何处理已发现的问题&#xff1f;层次聚类Lance-Williams 算法Lance-Williams 公式在Scipy中实现 示例DBSCANDBSCAN 算法 聚类…...

深度学习中的数据增强:提升食物图像分类模型性能的关键策略

深度学习中的数据增强&#xff1a;提升食物图像分类模型性能的关键策略 在深度学习领域&#xff0c;数据是模型训练的基石&#xff0c;数据的数量和质量直接影响着模型的性能表现。然而&#xff0c;在实际项目中&#xff0c;获取大量高质量的数据往往面临诸多困难&#xff0c;…...

QT设计权限管理系统

Qt能够简单实现系统的权限设计 首先我们需要一个登陆界面 例如这样 然后一级权限&#xff0c;可以看到所有的内容&#xff0c;不设置菜单栏的隐藏。 然后其他权限&#xff0c;根据登陆者的身份进行菜单栏不同的展示。 菜单栏的隐藏代码如下&#xff1a; ui->actionuser-…...

从上帝视角看文件操作

1.为什么使用文件? 如果没有文件,我们写的程序中的数据是存储在电脑的内存中,当程序退出时,内存被回收后,数据就丢失了,等下次运行程序,是无法看到上次程序的数据的。(比如我们在程序中写通讯录时,联系人的相关数据都是放在内存中的,当程序退出时,这些数据也会随之消…...

【51单片机6位数码管显示时间与秒表】2022-5-8

缘由数码管 keil proteus 为什么出现这种情况呢&#xff1f;-编程语言-CSDN问答 #include "reg52.h" unsigned char code smgduan[]{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0,64}; //共阴0~F消隐减号 unsigned char cod…...

从头训练小模型: 4 lora 微调

1. LoRA (Low-Rank Adaptation) LoRA是一种高效的参数高效微调&#xff08;Parameter-Efficient Fine-Tuning, PEFT&#xff09;方法&#xff0c;原理是通过低秩分解的方式对预训练模型进行微调。 相比于全参数微调&#xff08;Full Fine-Tuning&#xff09;&#xff0c;LoRA…...

前端开发,文件在镜像服务器上不存在问题:Downloading binary from...Cannot download...

问题与处理策略 问题描述 在 Vue 项目中&#xff0c;执行 npm i 下载依赖时&#xff0c;报如下错误 Downloading binary from https://npm.taobao.org/mirrors/node-sass//v4.14.1/win32-x64-72_binding.node Cannot download "https://npm.taobao.org/mirrors/node-sa…...