Loading

[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个点,state101表示第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;
}

其他

posted @ 2024-09-15 10:23  TommyJin  阅读(15)  评论(0)    收藏  举报