T1 组合数题目

 解:这题好像如果知道一个公式,应该就可以做了吧。。。

重点在于不知道。。。。。

程序:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
int a[20000],b[20000],c[2010][2010],f[2010][2010];
int t,k,n,m;
int main()
{
  scanf("%d%d",&t,&k);
  for (int i=1;i<=t;i++)
  {
      scanf("%d%d",&a[i],&b[i]);
      n=max(n,a[i]);
      m=max(m,b[i]);
  }
  for (int i=0;i<=2009;i++)
   for (int j=0;j<=2009;j++)
   c[i][j]=-1;
  for (int i=0;i<=n;i++) c[i][1]=i%k;
  for (int i=0;i<=n;i++) c[i][i]=1%k;
  for (int i=1;i<=n;i++)
   for (int j=2;j<=min(m,i);j++)
   if (i!=j)
       c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;
  for (int i=1;i<=n;i++)
   for (int j=1;j<=m;j++)
   {
    f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1];
    if (c[i][j]==0) f[i][j]++;
   }
  for (int i=1;i<=t;i++)
  if ((a[i]==0)||(b[i]==0)) printf("0\n");
  else printf("%d\n",f[a[i]][min(a[i],b[i])]); 
  return 0;

 

T2 蚯蚓 

  这题第一个想法就是用堆来储存,每次都挑出最大的那只,然后切,再放回去。为了解决每只蚯蚓每秒都会增长,放入堆的时候需减去总共已增长的长度,拿出的时候再加上,就解决了这个问题。但是这样写的话,我就只写了55分。。。。

  然后考虑就会发现,我们把当前的蚯蚓切成了t1和t2,因为蚯蚓每秒都在增长,所以保证t1一定大于下一次被切出的t1,t2一定大于下一次被切出的t2,只是我们不知道各个t1和t2间的大小关系罢了,那么我们就来维护三个队列,开始时,第一个队列为蚯蚓初始长度,并且由大到小排列,第二第三都为空,我们每次选取三个队首中最长的那条蚯蚓进行切分,并把得到的分开的两条蚯蚓分别放回第二和第三队列,反正第二个队列与第三个队列的单调性并不需要去维护就可以保持,这样就免除了之前每次都要logn维护,这样做m次操作。在最后把三个队列合并(切记,不能为了省事就把它们放一个数组里面后用快排,因为三个队列中都是各自有序的,这会导致快排退化,我就因为懒这么做了,然后。。。。还是老老实实两个两个合并吧),然后输出就好了。。

不过在洛谷上是对了,可是在bzoj上,是Presentation_Error,意思是答案基本正确,但有一些细节错误,总之就是一个多空格或者多空行的错误,然而我找不到我哪儿多了。。。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
inline int read()
{
    char c;
    while((c<'0')||(c>'9')) c=getchar();
    int ef = 0;
    while((c>='0')&&(c<='9'))
    {
      ef=ef*10+c-48;
      c=getchar();
    }
    return ef;
}
int t1,t2,t3,n,m,l,t,x,sum,u,v,h1,h2,h3,tot;
long long ans;
int q1[8000001],q2[8000001],q3[8000001],b[8000001];
bool cmp(const int x,const int y) {return (x>y);}
void find1()
{
    int maxx=-2147438646;
    int tx=0;
    if ((q1[h1]>maxx)&&(h1<=t1)){maxx=q1[h1]; tx=1;}
    if ((q2[h2]>maxx)&&(h2<=t2)){maxx=q2[h2]; tx=2;}
    if ((q3[h3]>maxx)&&(h3<=t3)){maxx=q3[h3]; tx=3;}
    if (tx==1) h1++;
    else if (tx==2) h2++;
    else if (tx==3) h3++;
    ans=maxx;
}
int main()
{
  n=read();m=read();l=read();u=read();v=read();t=read();
  h1=h2=h3=1; 
  t1=n;t2=t3=0;
  for (int i=1;i<=n;i++) q1[i]=read();
  sort(q1+1,q1+n+1,cmp);
  sum=0;
  int now2,now,now1;
  tot=0;
  int tu=t;
  for (int i=1;i<=m;i++)
  {
      find1();
      ans=ans+tot;
      now=(int)(ans*u/v);
      tot+=l;
      if ((i==tu)&&(tu+t<=m)) {printf("%lld ",ans);tu+=t;}
      else if (i==tu) {printf("%lld",ans);tu+=t;}
      q2[++t2]=now-tot;
      q3[++t3]=ans-now-tot;
  }
  int sum=0;
  cout<<endl;
  tu=t;
  while ((h1<=t1)||(h2<=t2))
  {
      if ((h1<=t1)&&(h2<=t2))
      {
        sum++;
        if (q1[h1]>q2[h2]){b[sum]=q1[h1];h1++;}
        else {b[sum]=q2[h2];h2++;}
    }
    else 
    {
        sum++;
        if (h2<=t2) {b[sum]=q2[h2];h2++;}
        else {b[sum]=q1[h1];h1++;}
    }
  }
  for (int i=1;i<=sum;i++) q2[i]=b[i];
  h2=1;t2=sum;
  sum=0;
  while ((h3<=t3)||(h2<=t2))
  {
      if ((h3<=t3)&&(h2<=t2))
      {
        sum++;
        if (q3[h3]>q2[h2]){b[sum]=q3[h3];h3++;}
        else {b[sum]=q2[h2];h2++;}
    }
    else 
    {
        sum++;
        if (h2<=t2) {b[sum]=q2[h2];h2++;}
        else {b[sum]=q3[h3];h3++;}
    }
  }
  
  for (int i=1;i<=sum;i++) 
  if ((i==tu)&&(tu+t<=sum)) {printf("%d ",b[i]+tot);tu+=t;}
  else if (i==tu) {printf("%d",b[i]+tot);tu+=t;}
  return 0;
}

 

T3 愤怒的小鸟

原谅我在做这道题之前没有做过状态压缩dp,总之就是用f[i]来表示在i的二进制表达下,打掉那些表示为1的小猪最少需几只鸟,比如说i为5,即二进制为101是,f[5]表示的是打掉第一只猪和第三只猪最少需要几只鸟。

具体的一些解释就在程序里讲好了。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
double x[30],y[30];
int t,n,m,f[300000],bird[20][20];
double eps=1e-9;
struct per{
    double a,b;
};
per calc(int i,int j)//这个纸上写写就好了。。。
{
  double x1=x[i],y1=y[i],x2=x[j],y2=y[j];
  per now;
  now.a=(y1*x2-y2*x1)/(x1*x1*x2-x2*x2*x1);
  now.b=(y1*x2*x2-y2*x1*x1)/(x1*x2*x2-x1*x1*x2);
  return now;
}
int check(double xx)//判等,因为==的精度太高。。。
{
  if (abs(xx)<=eps) return 0;
  else return 1;
}
int main()
{
  scanf("%d",&t);
  for (int pop=1;pop<=t;pop++)
  {
      scanf("%d%d",&n,&m);
      for (int i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]);
       //首先输入每只猪的坐标
      for (int i=0;i<=299999;i++) f[i]=2100000000;
      for (int i=1;i<=19;i++)
       for (int j=1;j<=19;j++)
       bird[i][j]=0;
//bird[i][j]表示同时经过(0,0),(xi,yi),(xj,yj)
//这三个点的二次函数能经过哪些点?首先如果存在这个函数的话,把这个数化为
二进制,它的第i位和第j位一定是1,表示它能经过这两个点,如果有一只猪(xk,yk)
也存在于这个二次函数上的话,那么这个数的第k位也就为1,不经过的话对应的位数就为零,
所以这个数表示的是经过这两点的函数能经过哪几只猪。
for (int i=1;i<=n;i++) for (int j=i+1;j<=n;j++) { per sum=calc(i,j); //计算同时经过(0,0),(xi,yi),(xj,yj)这个二次函数对应的a和b; if (sum.a>=0) continue; //如果开口朝上就不存在抛物线了,所以除去 for (int k=1;k<=n;k++) if (check(sum.a*x[k]*x[k]+sum.b*x[k]-y[k])==0) bird[i][j]=bird[i][j]|(1<<(k-1)); //看看有哪些猪是位于这条函数上的,如果位于,就把对应的二进制位数变为零 } f[0]=0; int tot=1<<n; for (int i=0;i<=tot-1;i++)//枚举当前状态,即达到i的二进制这种情况下,至少需要几只小鸟。 for (int j=1;j<=n;j++) if ((i&(1<<(j-1)))==0)//找到标号最前面的那只还未被打掉的小猪。 { for(int k=j+1;k<=n;k++) //枚举它之后的还没被打掉的小猪 { if ((i&(1<<(k-1)))==0) f[i|bird[j][k]]=min(f[i|bird[j][k]],f[i]+1); //看看打掉这两只小猪以及与他们两位于同一抛物线上的小猪,和原来i枚举的那些小猪的最小步数能不能被更新 } f[i|(1<<(j-1))]=min(f[i|(1<<(j-1))],f[i]+1); //有可能只有j一个没被打掉,所以还要考虑这种情况 break; //如果这个程序不加这个break,就会因超时失去三十分。。。
但很明显一眼看上去会认为这是不对的,因为它会少枚举很多的情况,
结果我们会发现j枚举的是i状态下编号最早的那只没被打的猪,你会发
现反正我们早晚都要消灭它,不如在最早的时候消灭好了,何必空着去
消灭后面的呢?(之后还是要回来消灭它)利用贪心原理,所以我们只
需找到第一个j就可以了,把第一只没被打的打掉。
} printf("%d\n",f[(1<<n)-1]); } return 0; }

 

posted on 2017-02-27 22:03  nhc2014  阅读(147)  评论(0)    收藏  举报