题目:https://www.luogu.org/problemnew/show/P2831
一开始想 n^3 贪心来着;
先按 x 排个序,那么第一个不就一定要打了么?
在枚举后面某一个,和它形成一条抛物线,选能顺便打掉最多的那个;
然后连样例都过不了...
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define eps 1e-9 using namespace std; int T,n,m,ans; bool vis[20]; struct N{ double x,y; }p[20]; bool cmp(N x,N y){return x.x<y.x;} int main() { scanf("%d",&T); while(T--) { memset(vis,0,sizeof vis); ans=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); sort(p+1,p+n+1,cmp); for(int i=1;i<=n;i++) { if(vis[i])continue; vis[i]=1; // printf("i=%d ",i); double x1=p[i].x, y1=p[i].y, xx=x1*x1, x2,y2,xy,a,b,aa=0,bb=0; for(int j=i+1;j<=n;j++) { int cnt=1,mx=0; if(vis[j]||p[j].x==p[i].x)continue; x2=p[j].x; y2=p[j].y; xy=x2*x2; // y2=y2/x2*x1; xy=xy/x2*x1; // a=(y1-y2)/(xx-xy); a=(y1*x2-y2*x1)/(x1*x2*(x1-x2)); b=(y1-a*xx)/x1; // printf("%lf %lf ",a,b); for(int k=j+1;k<=n;k++) { if(vis[k])continue; double tx=p[k].x,ty=p[k].y; if(a*tx*tx+b*tx-ty<eps)cnt++; } if(cnt>mx)mx=cnt,aa=a,bb=b; } if(aa||bb) for(int j=i+1;j<=n;j++) if(fabs(aa*p[j].x*p[j].x+bb*p[j].x-p[j].y)<eps)vis[j]=1; ans++; } printf("%d ",ans); } return 0; }
仔细想想,贪心可能是错的;
正解是状压DP,DP真是与贪心相对的正确解法啊...
n 很小,所以想到状压,记录所有可能的抛物线的打猪情况;
然后类似背包转移即可...
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define eps 1e-8 using namespace std; int T,n,m,f[262900],g[500],tot; double x[20],y[20]; bool ck(int k,double a,double b) { return fabs(a*x[k]*x[k]+b*x[k]-y[k])<eps; } int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]); memset(g,0,sizeof g); double a,b; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { if(x[i]==x[j])continue; a=(y[i]*x[j]-y[j]*x[i])/(x[i]*x[j]*(x[i]-x[j])); b=(y[i]-a*x[i]*x[i])/x[i]; if(a>=0)continue;//>=而非> ! tot++; for(int k=1;k<=n;k++) if(ck(k,a,b))g[tot]|=(1<<(k-1)); } for(int i=0;i<n;i++)g[++tot]|=(1<<i); sort(g+1,g+tot+1); tot=unique(g+1,g+tot+1)-g-1; memset(f,0x3f,sizeof f); f[0]=0; for(int i=0;i<(1<<n);i++) for(int j=1;j<=tot;j++) f[i|g[j]]=min(f[i|g[j]],f[i]+1); printf("%d ",f[(1<<n)-1]); } return 0; }