[2024-04-01]P2831 [NOIP2016 提高组] 愤怒的小鸟
题目解释
在一个二维平面上,有若干个点(这里用小猪比喻),目标是用最少的抛物线数量将这些点全部“消灭”。每条抛物线可以消灭在它上面的所有点。
样例输入
2
2 0
1.00 3.00
3.00 3.00
5 2
1.00 5.00
2.00 8.00
3.00 9.00
4.00 8.00
5.00 5.00
样例输出
1
1
问题分析
- 状态定义:动态规划的状态可以定义为
dp[state],其中state是一个二进制数,表示哪些点已经被消灭。例如,如果有3个点,state为101表示第1个和第3个点已经被消灭。 - 状态转移:对于每个状态,我们可以枚举下一个要消灭的点,或者是一对点(如果它们可以通过一条抛物线同时消灭)。这样,我们可以从当前状态转移到一个新的状态。
- 初始化:每个点单独被消灭的情况作为初始化状态,即
dp[1<<i] = 1。 - 目标:最终的目标是找到
dp[(1<<n)-1]的最小值,即所有点都被消灭的情况。
伪代码
int main(){
read();
for(i):dp[1<<(i-1)]=1;//射一只小猪消耗一只小鸟
for(state)//枚举状态
{
for(pig_1)//第一只小猪
{
for(pig_2)//第二只小猪
{
dp[state|cal[pig_1][pig_2]]
=min(self,dp[state]+1);
}
dp[state|(1<<(pig_1-1))]=min(self,dp[state]+1);
//单独消灭pig_1
}
}
print(dp[(1<<m)-1]);
return 0;
}
伪代码解释:
首先,对每个小猪的组合,计算出能同时消灭它们的小鸟的轨迹,并存储在 cal[pig_1][pig_2] 中。
- 然后,通过状态压缩的方式,枚举所有可能的状态,其中
dp[state]表示在状态state下所需的最小小鸟数量。 - 对于每个状态,分别考虑将一只小鸟单独射击某只小猪,或者射击一对小猪的情况,取最小值作为当前状态所需的小鸟数量。
- 最后输出状态为所有小猪都被消灭的状态所需的最小小鸟数量。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=300010;
int t,n,m;
struct node {
double x,y;
} pig[20];
int st[20][20],judge[20];
int dp[maxn];
int calc(int d1,int d2) {
double x_1=pig[d1].x,y_1=pig[d1].y;
double x_2=pig[d2].x,y_2=pig[d2].y;
if(x_1==x_2) return 0;
double a=(y_1*x_2-y_2*x_1)/(x_1*x_1*x_2-x_2*x_2*x_1),b=(y_1*x_2*x_2-y_2*x_1*x_1)/(x_1*x_2*x_2-x_1*x_1*x_2);
if(a>=0) return 0;
int res=0;
judge[d1]=judge[d2]=1;
for(int i=1; i<=n; ++i) {
double x=pig[i].x,y=pig[i].y;
if(fabs(a*x*x+b*x-y)<1e-7)
res|=1<<(i-1),dp[1<<(i-1)]=dp[res]=1;
}
return res;
}
int main() {
cin>>t;
while(t--) {
memset(dp,67,sizeof(dp));
memset(judge,0,sizeof(judge));
cin>>n>>m;
for(int i=1; i<=n; ++i) {
scanf("%lf%lf",&pig[i].x,&pig[i].y);
dp[1<<(i-1)]=1;
}
for(int i=1; i<=n; ++i)
for(int j=1; j<i; ++j)
st[i][j]=calc(i,j);
for(int i=1; i<=(1<<n)-1; ++i) {
for(int j=1; j<=n; ++j) {
if( ((i>>(j-1))&1)== 1 )continue;
if(!judge[j]) {
dp[i|(1<<(j-1))]=min(dp[i|(1<<(j-1))],dp[i]+1);
continue;
}
for(int k=1; k<j; ++k) {
if( (i&(1<<(k-1))) == 1 )continue;
dp[i|st[j][k]]=min(dp[i|st[j][k]],dp[i]+1);
}
dp[i|(1<<(j-1))]=min(dp[i|(1<<(j-1))],dp[i]+1);
}
}
printf("%d\n",dp[(1<<n)-1]);
}
return 0;
}

浙公网安备 33010602011771号