[搜索] HNOI2002 彩票

题目描述

某地发行一套彩票。彩票上写有1到M这M个自然数。彩民可以在这M个数中任意选取N个不同的数打圈。每个彩民只能买一张彩票,不同的彩民的彩票上的选择不同。

每次抽奖将抽出两个自然数X和Y。如果某人拿到的彩票上,所选N个自然数的倒数和,恰好等于X/Y,则他将获得一个纪念品。

已知抽奖结果X和Y。现在的问题是,必须准备多少纪念品,才能保证支付所有获奖者的奖品。

输入输出格式

输入格式:

输入文件有且仅有一行,就是用空格分开的四个整数N,M,X,Y。

输出格式:

输出文件有且仅有一行,即所需准备的纪念品数量。

1≤X, Y≤100,1≤N≤10,1≤M≤50。

输入数据保证输出结果不超过10^5。

输入输出样例

输入样例#1:

2 4 3 4

输出样例#1:

1

题解

本来是本着刷splay的心去刷这道题的,结果一看题目感觉是搜索,点开算法标签\(splay\)????
再点开讨论和题解(发现并没有人用splay做)看了一下应该是有人恶搞(人才.....)
回到这道题,首先直接爆搜肯定是不行的,题目所给的m有50,最坏时间复杂度应该有\(O((m-1)!)\).显然过不了,马上想到剪枝,这个剪枝应该还是比较容易想到的

  1. 如果当前结果\(tot\)>\(x/y\),返回
  2. 如果当前结果\(tot\)+后续能加的最大值<\(x/y\),返回
  3. 如果当前结果\(tot\)+后续能加的最小值>\(x/y\),返回
  4. 用个前缀和优化一下就可以了

就这三个最优性剪枝就已经能A掉这道题了,注意一下,数组比题目所给的50开大点,我也不知道为什么开60只有70分(T三个点),开70A了,开100又比开70快300ms

Code

#include<bits/stdc++.h>
#define in(i) (i=read())
using namespace std;
int read() {
  int ans=0,f=1; char i=getchar();
  while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
  while(i>='0' && i<='9') {ans=(ans<<1)+(ans<<3)+i-'0'; i=getchar();}
  return ans*f;
}
int n,m,x,y,ans;
double sta,eps=1e-10;
int vis[100];
double sum[100];
void dfs(int x,int last,double tot) {
  if(tot-sta>eps) return;
  if((tot+sum[last+n-x+1]-sum[last]+eps)<sta) return;
  if((tot+sum[m]-sum[m-(n-x+1)])>sta+eps) return;
  if(x==n+1) {
    if(fabs(tot-sta)<=eps) ans++;
    return;
  }
  for(int i=last+1;i<=m;i++) {
    if(!vis[i]) {
      vis[i]=1;
      dfs(x+1,i,tot+1.0/(double)i);
      vis[i]=0;
    }
  }
}
int main()
{
  in(n); in(m); in(x); in(y);
  for(int i=1;i<=m;i++) sum[i]=sum[i-1]+(1.0/(double)i);
  sta=(double)x/(double)y;
  dfs(1,0,0);
  printf("%d\n",ans);
  return 0;
}

博主蒟蒻,随意转载.但必须附上原文链接

http://www.cnblogs.com/real-l/

猜你喜欢

转载自www.cnblogs.com/real-l/p/9351230.html
今日推荐