洛谷P2831 NOIP2016 愤怒的小鸟

题目

传送门

思路

个人感觉我的思路并不是很优

m貌似没什么用(可能是部分分),直接忽略

但是看到n最大才去到18,不是状压就是爆搜,我采用的是DFS维护状压DP的转移(其实有点像记忆化搜索)

注意:为了方便状压,以下小猪的编号从0~n-1

设状态k二进制下第i为表示:第i只小猪的消灭情况0:没消灭,1:消灭,f[k]表示状态为k时最少用的小鸟数量

状态转移:

\[f_k=\min(f_{s}+1) \]

其中s满足(s | k == k)且对于所有i,i满足((k >> i)&1==1 ,(s>>i)&1==0),编号为i的小猪在同一条合法的抛物线上(即可以用同一只小鸟解决)

特别地,当k的二进制下只有一位为"1"时,f[k]=1,k==0时,f[k]=0.

所以对于每个状态k,我们需要枚举两只小猪i,j,再去掉和小猪i,j在同一条合法抛物线上的小猪,时间复杂度O(n3×2n)是会超时的

优化:

  1. 预处理小猪两两组合产生的抛物线:常数级优化

  2. 考虑到在同一条抛物线上的小猪两两组合产生的抛物线是一样的,不必重复枚举,所有对于每一次dfs转移,定义vis[i] [j]表示是有枚举过小猪i,j,若找到小猪k,k与i,j在同一条抛物线上

 vis[i][k] = vis[k][i] = vis[j][k] = vis[k][j] = true

​ 玄学级优化

  1. 题解上有说预处理所有和i,j在同一条抛物线上的小猪,但是我没写出来,题解传送门,优化为O(n2×2n)

然后发现还是有两个TLE我一气之下开了O2把这题切了

一些小问题

精度:

亲测1e-4是不够的,最后我开的是1e-8

抛物线解析式:

具体查看初二的数学课本,不做赘述

inline void cal(int i , int j , double &a , double &b) {
	if(x[i] == 0 || x[j] == 0 || x[i] == x[j]) {
		a = 999;//不合法
		return;
	}
	a = (y[i] * x[j] - y[j] * x[i])/(x[i] * x[j] * (x[i] - x[j]));
	b = y[i] / x[i] - a * x[i];
}

tips:

测试数据

代码

我的(1.cpp)

#include <iostream>
#include <cstdio>
#include <cstring>
#define rr register
//#pragma GCC optimize(2)
using namespace std;
int read() {
	int re = 0 , sig = 1;
	char c = getchar();
	while(c < '0' || c > '9'){
		if(c == '-')sig = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9'){
		re = (re << 1) + (re << 3) + c - '0';
		c = getchar();
	}
	return re * sig;
}
int n;
double x[20] , y[20];
double aa[20][20] , bb[20][20];
inline void cal(int i , int j , double &a , double &b) {
	if(x[i] == 0 || x[j] == 0 || x[i] == x[j]) {
		a = 9;
		return;
	}
	a = (y[i] * x[j] - y[j] * x[i])/(x[i] * x[j] * (x[i] - x[j]));
	b = y[i] / x[i] - a * x[i];
}
inline double fabss(double a) {
	return a < 0  ? -a : a;
}
inline bool check(double a , double b , int i) {
	return fabss(a * x[i] * x[i] + b * x[i] - y[i]) < 1e-8;
}
inline int count_(int x){
	int cnt = 0;
	while(x != 0){
		x = x & (x - 1);
		cnt++;
	}
	return cnt;
}
void print(int x) {
	if(x >= 2)
		print(x / 2);
	putchar(x % 2 + 48);
}
int f[1 << 20];
int dfs(rr int stat) {
	if(f[stat] != -1)
		return f[stat];
	double a , b;
	int count_res = count_(stat);
	if(count_res == 0)
		return 0;
	if(count_res == 1)
		return 1;
	if(count_res == 2){
		int i = 0 , j = 0 , tmp = stat;
		while(1) {
			if(tmp & 1)
				break;
			tmp >>= 1 , ++i , ++j;
		}
		tmp >>= 1;
		++j;
		while(1) {
			if(tmp & 1)
				break;
			tmp >>= 1 , ++j;
		}
		a = aa[i][j];
		b = bb[i][j];
//		cout << i << '\t' << j << endl;
		f[stat] = (a >= 0 ? 2 : 1);
		return f[stat];
	}
	
	bool vis[20][20];
	memset(vis , 0 , sizeof(vis));
	
	f[stat] = count_res;
	rr int tmp , temp;
	for(rr int i = 0 ; i < n ; ++i)
		if((stat >> i) & 1)
			for(rr int j = 0 ; j < n ; ++j) {
				if(vis[i][j])continue;
				vis[i][j] = vis[j][i] = true;
				
				if(((stat >> j) & 1) == 0)	continue;
				if(i == j)continue;
				a = aa[i][j];
				b = bb[i][j];
				if(a >= 0)continue;
				tmp = stat;
				tmp ^= (1 << i);
				tmp ^= (1 << j);
				for(rr int k = 0 ; k < n ; ++k)
					if(((tmp >> k) & 1 ) == 1 && check(a , b , k)) {
						tmp ^= (1 << k);
						vis[i][k] = vis[k][i] = vis[j][k] = vis[k][j] = true;
					}
				
				temp = (f[tmp] == -1 ? dfs(tmp) + 1 : f[tmp] + 1);
				if(temp < f[stat]) {
					f[stat] = temp;
				}
			}
	return f[stat];
}
int main() {
//	freopen("angrybirds13.in" , "r" , stdin);
	int T = read();
	while(T--) {
		memset(f , -1 , sizeof(f));
		n = read();
		read();
		for(int i = 0 ; i < n ; i++)
			cin >> x[i] >> y[i];
		for(int i = 0 ; i < n ; ++i)
			for(int j = 0 ; j < n ; ++j)
				if(i != j)
					cal(i , j , aa[i][j] , bb[i][j]);
		cout << dfs((1 << (n)) - 1) << endl;
	}
	return 0;
}

自测程序(compare.cpp)

请将下载的数据,待测程序,自测程序放在同一文件夹下

#include <bits/stdc++.h>
using namespace std;
string itos(int x) {
	string s;
	while(x != 0){
		s = (char)(x % 10 + 48) + s;
		x /= 10;
	}
	return s;
}
int main() {
	for(int i = 1 ; i <= 20 ; i++) {
		printf("No.%d\n" , i);
		int t = clock();
		system(("1.exe < " + (string)"angrybirds" + itos(i) + ".in " + " > output.txt").c_str());
		printf("%dms\n" , clock() - t);
		if(system(("fc output.txt " + (string)"angrybirds" + itos(i) + ".ans").c_str())){
			cout << "WA!\n";
			system("pause");
		}//*/
	}
	return 0;
}
posted @ 2020-11-11 11:35  追梦人1024  阅读(89)  评论(0编辑  收藏  举报