• P2831 愤怒的小鸟 题解


    CSDN同步

    原题链接

    简要题意:

    平面直角坐标系的第一象限有若干绿猪,小鸟要通过若干条函数解析线来消灭它们。每个小鸟可以把所有 (y=ax^2 + bx (a<0)) 上的 ((x,y)) 的所有绿猪消灭,当然没有绿猪就是白走。问最少多少次后可以消灭所有的绿猪。(T) 组数据。

    前记

    实际上,之前写过一篇 骗分导论,里面有点口出狂言,既然自己说了能 (60pts) 那还不来填坑?

    算法一

    对于 (70 \%) 的数据,(n leq 12).

    实际上,直接爆搜即可。

    怎么搜?对于每个搜索状态,你可以用一个 ( ext{vector}) 来记录当前 剩下绿猪的坐标,并记录当前步数。

    由于 ( ext{vector}) 可以方便的删除元素,我们枚举当前 ( ext{vector}) 的两个坐标并计算出 (a,b) 的值,然后判断 (a<0),合法则将所有 (y=ax^2+bx) 上的绿猪删除,进入下一层。

    这样的话,如果你没有开 ( ext{long double}),可以得到 (55pts).

    开了之后就是 (70pts). 脸黑啊

    时间复杂度:(O( ext{70pts})).

    期望得分:(60pts). 实际得分:(70pts).

    Link 代码

    暴力最强大!

    算法二

    对于 (100 \%) 的数据,(n leq 18)(T leq 5).

    显然,我们会发现在上面的搜索中,冗余的状态太多了。

    比方说你先把 (1,4) 一起打下来,再把 (2,3) 一起打下来,和把打它们的顺序换一下,本质有什么区别呢?——没有,但是搜索中会大量出现类似 重复搜索同一个状态 的现象,就会想斐波那契数列那样越滚越大,最终 ( ext{TLE}).

    就上面一点而言,假设造个数据,保证每两头绿猪都无法一起打下,即每次只能干掉一个,那这个程序的时间复杂度 (cdots cdots)

    所以我们想解决这个问题,但是你认为如果用 ( exttt{map<vector<pair<db,db> > >}) 实在是不好,还多一个 (log),就和正解阴差阳错了。所以我们想到,记忆化搜索的本质不就是 ( ext{dp})?而枚举状态的 ( ext{dp}) 不就是 (cdots cdots)

    状态压缩 ( ext{dp}),简称状压!

    我们可以用一个二进制数来表示当前状态(显然只有打下和没打下两个情况),然后先把所有解析线(至少能打下 (2) 头绿猪的)统计在一起,然后枚举状态转移即可。但这个说法不太清晰,需要建立理论模型。

    (f) 存储状态,(f_0) ~ (f_{{2^n}-1}) 即为所有状态。把当前解析线能消灭的猪也有一个二进制数存在 (s) 里,每次枚举状态的时候,对于每个 (s) 都要考虑一个转移:

    f[k|s[i]]=min(f[k|s[i]],f[k]+1);
    

    即如果当前线包含 (i) 则直接用线解决;否则 (+1).

    但是你发现会有一些绿猪,只能单独被打,不能和别的猪一起被打。

    对于这样的猪,我们需要再枚举所有的猪,将它更新一遍。

    f[k|(1<<(i-1))]=min(f[k|(1<<(i-1))],f[k]+1);
    

    显然对于第 (k) 个状态,当前所有猪被打的答案可以直接更新掉,解决了这个问题。

    最后(f_{{2^n}-1}) 就是答案。

    时间复杂度:(O(2^n cdot n^2)).(不过 ( ext{AThousandSuns}) 提出了一种 (O(2^n cdot n)) 的算法,详见 AThousandSuns 的洛谷博客

    期望得分:(85pts). 实际得分:(100pts).

    理论上跑满了约为 (8.4 imes 10^7),挺危险的,擦边球过去了。

    这里你就会明白为什么记忆化搜索过不了,因为,记忆化用 ( ext{map}) 的时间复杂度是 (O(2^n cdot n^2 log n)),你就多了 (4) 倍的常数,成功到了 (2.7 imes 10^8) 一个更危险的级别。当然常数好卡过去我也不说什么了

    注意一下:计算解析式的时候可能存在一定精度问题,因此保留 (10^{-6}) 即可。

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    #define db long double
    const int N=1e6+1; 
    
    inline int read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	int x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}
    
    inline void write(int x) {
    	if(x<0) {putchar('-');write(-x);return;}
    	if(x<10) {putchar(char(x%10+'0'));return;}
    	write(x/10);putchar(char(x%10+'0'));
    }
    
    int n,m,T,cnt=0;
    int f[N],s[501];
    db x[101],y[101];
    
    int main() {
    	T=read(); while(T--) {
    		n=read(),m=read();
    		memset(f,0x3f,sizeof(f));
    		memset(s,0,sizeof(s));
    		cnt=0; f[0]=0; //初始化
    		for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
    		for(int i=1;i<=n;i++)
    		for(int j=i+1;j<=n;j++)
    			if(x[i]-x[j]) {
    				double a=(y[i]-y[j]*x[i]/x[j])/x[i]/(x[i]-x[j]);
    				double b=(y[i]*x[j]*x[j]/x[i]/x[i]-y[j])/(x[j]*x[j]/x[i]-x[j]);
    				if(a<0) { //计算合法的解析式
    					cnt++;
    					for(int k=1;k<=n;k++)
    						if(fabs(a*x[k]*x[k]+b*x[k]-y[k])<=1e-6) s[cnt]|=(1<<(k-1)); 
    				} //记录解析式
    			}
    		for(int k=0;k<=(1<<n)-1;k++) {
    			for(int i=1;i<=cnt;i++) f[k|s[i]]=min(f[k|s[i]],f[k]+1);
    			for(int i=1;i<=n;i++) f[k|(1<<(i-1))]=min(f[k|(1<<(i-1))],f[k]+1);
    		} printf("%d
    ",f[(1<<n)-1]);
    	} 
    	return 0;
    }
    
    
    
  • 相关阅读:
    vs编译器好多下划波浪线但不报错
    scala学习——(1)scala基础(下)
    scala学习——(1)scala基础(上)
    未能正确加载包“Microsoft.Data.Entity.Design.Package.MicrosoftDataEntityDesignPackage(转)
    如何完全卸载VS2010(亲自体验过) (转)
    2_C语言中的数据类型 (十)数组
    C++ STL 学习笔记__(8)map和multimap容器
    opencv配置(转)
    2_C语言中的数据类型 (九)数组
    郑捷《机器学习算法原理与编程实践》学习笔记(第五章 梯度寻优)5.2 Logistic梯度下降法
  • 原文地址:https://www.cnblogs.com/bifanwen/p/13089466.html
Copyright © 2020-2023  润新知