[关闭]
@happyforever 2018-11-05T14:26:41.000000Z 字数 7670 阅读 487

11.5清北学堂"18金秋Problem1"解题报告

清北学堂 解题报告


各题状况

期望得分:
实际得分:

T1

概率,好不想做啊。。
算了随便写个方程想想转移。。。
首先考虑不打假赛是最优策略然后写
。。。(十五分钟后)
我转移写出来了????
真的假的
跑跑试试
它对了?

写个挑几场打假赛然后扔上去吧

T2

哇这个数据范围看着都发憷
分的背包还行

每次随机挑选几个物品看能凑成多少?
跑个次。。。
肯定能骗到分的吧qwq

那个就删掉吧。。。
一场考试写两个随机算法真的是想日神仙吗

T3

这这这这。。。
不就是随便维护吗
敲完)
稳了稳了

image.png
image.png

改对之后
image.png

这就很难受了啊。。。。

题目及考场代码

T1

image.png

image.png

  1. /*
  2. * 如果一直赢下去那么答案就是所有可能然后求出期望
  3. *
  4. * dp[i][j]表示前i场赢了j场的概率
  5. * dp[i][j]=dp[i-1][j]*(1-p[i][j])+dp[i-1][j-1]*p[i][j-1];
  6. * 同时每次j循环开始前单独更新dp[i][0]=dp[i-1][0]*(1-p[i][0]);
  7. * 假设不打假赛那这样做就是结果
  8. *
  9. *
  10. * f[i][j][k]表示前i场比赛赢了j场其中k场买外围
  11. *
  12. * 但是这个做法过不了啊。。。。
  13. * 第一维滚动掉之后就开得下空间了,但是O(n^3)的。。。。
  14. * 就这边这破机子有点悬
  15. * 吔
  16. *
  17. *
  18. * mmp
  19. * rand大法好
  20. *
  21. *
  22. * 算了算了。。
  23. * 有T2用rand就够了
  24. * rp耗不起
  25. */
  26. #include <cstdio>
  27. #include <cstring>
  28. const int N=1001;
  29. int n;
  30. double ans,p[N][N],dp[N][N];
  31. bool vis[N];
  32. int main()
  33. {
  34. freopen("game.in","r",stdin);
  35. freopen("game.out","w",stdout);
  36. scanf("%d",&n);
  37. for(int i=1;i<=n;++i)
  38. for(int j=0;j<i;++j)
  39. scanf("%lf",&p[i][j]);
  40. if(n<=2)
  41. {
  42. double emp=0;
  43. emp+=p[1][0]*p[2][1]*2;//两局都赢
  44. emp+=p[1][0]*(1-p[2][1])*1;//第一局赢,第二局输
  45. emp+=(1-p[1][0])*p[2][0]*1;//第一局输,第二局赢
  46. ans=emp;
  47. //第一局打假赛
  48. emp=p[2][0]*1;
  49. if(emp>ans)ans=emp;
  50. //第二局打假赛
  51. emp=p[1][0]*1;
  52. if(emp>ans)ans=emp;
  53. printf("%.2lf",ans);
  54. }
  55. else
  56. {
  57. // 骗分:假设所有比赛都不买外围是最优情况
  58. dp[0][0]=1;
  59. for(int i=1;i<=n;++i)
  60. {
  61. dp[i][0]=dp[i-1][0]*(1-p[i][0]);
  62. for(int j=1;j<=i;++j)
  63. dp[i][j]=dp[i-1][j]*(1-p[i][j])+dp[i-1][j-1]*p[i][j-1];
  64. }
  65. for(int i=1;i<=n;++i)
  66. ans+=dp[n][i]*i;
  67. printf("%.2lf",ans);
  68. }
  69. fclose(stdin);fclose(stdout);
  70. return 0;
  71. }

T2

image.png

image.png

  1. /*
  2. * 40分的01背包
  3. * 100分。。。可能是状压?
  4. * 鬼知道。
  5. *
  6. * 我大概可以。。。维护一个大小的数组?
  7. * dfs枚举所有可能的组合?
  8. * 2^40次方。。
  9. * 。。。
  10. * 想屁吃。。。
  11. *
  12. *
  13. * emmmm
  14. * 如果dfs可以剪枝掉非常多的情况,就dfs
  15. * 否则跑5e5次rand,rand出选择的物品
  16. * 所有rand出的情况取最优
  17. */
  18. #include <cstdio>
  19. #include <cstdlib>
  20. #include <cstring>
  21. //#include <vector>
  22. #include <algorithm>
  23. const int N=41,M=1e4+1;
  24. int n,x,v[N],dp[M],ans;
  25. bool vis[N];
  26. //std::vector<int> qwq;
  27. void dfs(int now,int w)
  28. {
  29. if(now>n)
  30. {
  31. if(w==x){printf("0");exit(0);}
  32. if(w>ans)
  33. ans=w;
  34. return ;
  35. }
  36. // 先把所有都选上应该是个不错的选择。。
  37. // printf("%d %d\n",now,w);
  38. if(w+v[now]<=x)
  39. dfs(now+1,w+v[now]);
  40. dfs(now+1,w);
  41. }
  42. void work()
  43. {
  44. memset(vis,false,sizeof vis);
  45. int emp=0;
  46. for(int s,i=1;i<=n;++i)
  47. {
  48. s=rand()%(n+1);
  49. vis[s]=true;
  50. }
  51. for(int i=1;i<=n;++i)
  52. if(vis[i])
  53. {
  54. if(emp+v[i]>x)
  55. continue ;
  56. emp+=v[i];
  57. }
  58. if(emp>ans)ans=emp;
  59. }
  60. inline int max(int x,int y)
  61. {return x>y?x:y;}
  62. int main()
  63. {
  64. freopen("cake.in","r",stdin);
  65. freopen("cake.out","w",stdout);
  66. scanf("%d%d",&n,&x);
  67. for(int i=1;i<=n;++i)
  68. scanf("%d",&v[i]);
  69. /*
  70. for(int i=1;i<=100000;++i)
  71. work();
  72. printf("%d",x-ans);
  73. */
  74. if(n<=20)
  75. {
  76. for(int i=1;i<=n;++i)
  77. for(int j=x;j>=v[i];--j)
  78. dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
  79. printf("%d",x-dp[x]);
  80. }
  81. else
  82. {
  83. // srand(time(0));
  84. // srand(19260817);
  85. srand(20010929);
  86. bool flag=false;
  87. for(int i=1;i<=n;++i)
  88. {
  89. if(ans+v[i]<=x)
  90. ans+=v[i];
  91. else flag=true;
  92. }
  93. if(flag)
  94. {
  95. std::sort(v+1,v+1+n);//排序对随机影响不大,但是需要在排序的基础上进行dfs剪枝效果的判断
  96. if(v[(n>>1)+1]>(x>>1))
  97. dfs(1,0);
  98. else
  99. for(int i=1;i<=500000;++i)//极限数据0.7s
  100. {
  101. if(ans==x){printf("0");goto E;}
  102. work();
  103. }
  104. }
  105. printf("%d",x-ans);
  106. }
  107. E: fclose(stdin);fclose(stdout);
  108. return 0;
  109. }

T3

image.png

image.png

  1. /*
  2. * 10分输出m^(h*w)
  3. * 20分手玩
  4. *
  5. *
  6. * 对于没有被限定的区域,其总方案数是m^(格子数)
  7. * 考虑每次询问实际是给一块矩形限制了上界,以及在最后时刻这个矩形内必须至少有一个是给定值
  8. * 考虑有一块矩形的上界都是v
  9. * 那么该矩形内的方案数为v^(格子数)-(v-1)^(格子数)
  10. * 把重叠的位置扣去,对每组询问记录其格子数
  11. *
  12. *
  13. * 突然发现调出来之后如果不删输出调试的注释,代码好丑陋啊。
  14. */
  15. #include <cstdio>
  16. #include <algorithm>
  17. inline int read()
  18. {
  19. int n=0,w=1;register char c=getchar();
  20. while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}
  21. while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();
  22. return n*w;
  23. }
  24. const int N=1e4+1,mod=1e9+7;
  25. int h,w,m,n,map[N][N];
  26. struct Node{
  27. int a,b,x,y,v,sum;
  28. bool operator<(const Node &gg)const
  29. {return v<gg.v;}
  30. }q[11];
  31. inline int ksm(long long x,int b)
  32. {
  33. long long res=1;
  34. for(;b;b>>=1,x=x*x%mod)
  35. if(b&1)
  36. res=res*x%mod;
  37. return res;
  38. }
  39. int main()
  40. {
  41. freopen("grid.in","r",stdin);
  42. freopen("grid.out","w",stdout);
  43. h=read(),w=read(),m=read(),n=read();
  44. int sum=h*w;
  45. for(int i=1;i<=h;++i)
  46. for(int j=1;j<=w;++j)
  47. map[i][j]=m+1;
  48. for(int i=1;i<=n;++i)
  49. {
  50. q[i]=(Node){read(),read(),read(),read(),read(),0};
  51. q[i].sum=(q[i].b-q[i].a+1)*(q[i].y-q[i].x+1);
  52. //数据保证x>a,y>b
  53. }
  54. // puts("GG");
  55. // 离线处理
  56. std::sort(q+1,q+1+n);
  57. for(int v,k=1;k<=n;++k)
  58. {
  59. v=q[k].v;
  60. for(int i=q[k].a;i<=q[k].x;++i)
  61. {
  62. for(int j=q[k].b;j<=q[k].y;++j)
  63. {
  64. // printf("%d ",map[i][j]);
  65. if(map[i][j]>v)
  66. map[i][j]=v,--sum;
  67. else --q[k].sum;
  68. }
  69. // puts("");
  70. }
  71. }
  72. // printf("%d",sum);
  73. int ans=ksm(m,sum);//没有被限制的区域的方案数
  74. for(int emp,i=1;i<=n;++i)
  75. {
  76. // printf("%d %d %d\n",emp,q[i].v,q[i].sum);
  77. emp=(ksm(q[i].v,q[i].sum)-ksm(q[i].v-1,q[i].sum)+mod)%mod;
  78. ans=ans*emp%mod;
  79. }
  80. printf("%d",ans);
  81. fclose(stdin);fclose(stdout);
  82. return 0;
  83. }

正解及代码

T1

最优策略就是不卖外围不打假赛,每一场都认认真真打
然后就期望最大了。。。
它范围和时限开那么大是误导人用的。。
太毒瘤了这人。

证明:最优策略就是认真打比赛
假设有这样一个状态
image.png
已经赢了
状态获胜的概率为状态获胜概率为状态获胜概率为
如果这场比赛获胜了那么会转移到,否则会转移到
考虑如果这场比赛打假赛那么最后期望会增加
如果这场比赛认真打那么会有几种情况:状态赢状态赢,状态赢状态输,状态输状态赢,状态输状态输
分别对应的期望是
不妨假设
那么这场比赛打假赛会增加的期望,这场比赛认真打会增加的期望
化简出来是


证毕

表示前场赢了场的概率

当然需要单独转移:
最后

  1. #include <cstdio>
  2. #include <cstring>
  3. const int N=1001;
  4. int n;
  5. double ans,p[N][N],dp[N][N];
  6. bool vis[N];
  7. int main()
  8. {
  9. freopen("game.in","r",stdin);
  10. freopen("game.out","w",stdout);
  11. scanf("%d",&n);
  12. for(int i=1;i<=n;++i)
  13. for(int j=0;j<i;++j)
  14. scanf("%lf",&p[i][j]);
  15. dp[0][0]=1;
  16. for(int i=1;i<=n;++i)
  17. {
  18. dp[i][0]=dp[i-1][0]*(1-p[i][0]);
  19. for(int j=1;j<=i;++j)
  20. dp[i][j]=dp[i-1][j]*(1-p[i][j])+dp[i-1][j-1]*p
  21. [i][j-1];
  22. }
  23. for(int i=1;i<=n;++i)
  24. ans+=dp[n][i]*i;
  25. printf("%.2lf",ans);
  26. fclose(stdin);fclose(stdout);
  27. return 0;
  28. }

T2

折半搜索
虽然无法接受,但是还是比较稳的
把所有物品分成两堆,第一堆有个,第二堆是剩下的那些
那么对这两堆物品分别求出能够组合出的价值和
考虑Meet in the middle
排序后用两个指针从两侧向中间夹逼找到最接近的和,或者在右边的一堆有一个,在左边的一堆里面找出小于等于的最大值

  1. #include <cstdio>
  2. #include <algorithm>
  3. const int N=1<<20|1,M=1e4+1;
  4. int x,n,up,a[N],v[41],ans;
  5. void dfs1(int now,int w)
  6. {
  7. if(now>up)
  8. {
  9. a[++a[0]]=-w;
  10. // printf("%d\n",w);
  11. return ;
  12. }
  13. if(w+v[now]<=x)
  14. dfs1(now+1,w+v[now]);
  15. dfs1(now+1,w);
  16. }
  17. int emp,tmp;
  18. void dfs2(int now,int w)
  19. {
  20. if(now>n)
  21. {
  22. // printf("%d\n",w);
  23. emp=w-x;
  24. if(emp<=a[a[0]])
  25. w-=a[std::lower_bound(a+1,a+1+a[0],emp)-a];
  26. if(w>ans)ans=w;
  27. return ;
  28. }
  29. if(w+v[now]<=x)
  30. dfs2(now+1,w+v[now]);
  31. dfs2(now+1,w);
  32. }
  33. int main()
  34. {
  35. freopen("cake.in","r",stdin);
  36. freopen("cake.out","w",stdout);
  37. scanf("%d%d",&n,&x);
  38. up=n>>1;
  39. for(int i=1;i<=n;++i)
  40. scanf("%d",&v[i]);
  41. dfs1(1,0);
  42. std::sort(a+1,a+1+a[0]);
  43. dfs2(up+1,0);
  44. printf("%d",x-ans);
  45. fclose(stdin);fclose(stdout);
  46. return 0;
  47. }

T3

对当前的图考虑有个矩形将其覆盖
对于没有被任何矩形覆盖的位置,假设其个数为,那么这部分没有被任何矩形覆盖的位置对答案的贡献为
而对于被矩形覆盖过的位置,其实质是将这些位置的值域限制在了“最大值”
那么离线之后每个位置的值域就会变成所有覆盖到这个位置的矩形的的最小值
考虑每个矩形对答案的贡献:假设矩形大小等于,那么对于这个矩形,假设忽略其“矩形内至少有一个值等于”的限制,那么总共有种构造方法,再减掉其中不合法的(没有任何一个位置的值等于)方案数
另外由于有的矩形会重合,所以需要对某些矩形扣掉一部分
这里给出一种离线方法:
注意到每个位置最后保留的都是覆盖过该位置的最小的矩阵的
首先把所有的操作都读入,读入时记录该操作覆盖的格子数;将所有操作按照从小到大排序,每次操作暴力枚举每个位置进行覆盖;如果枚举到了某个之前有值的位置,那么说明这个位置需要从当前矩阵中扣出,也就是当前矩阵的

考虑无解的情况:某两个矩形是包含关系且被包含的矩形的要大于外面包含它的矩形的
另外这题需要二维离散化。。。。。

下面给出没有二维离散化过的代码(会个点,因为复杂度是的)

  1. #include <cstdio>
  2. #include <algorithm>
  3. inline int read()
  4. {
  5. int n=0,w=1;register char c=getchar();
  6. while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}
  7. while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();
  8. return n*w;
  9. }
  10. const int N=1e4+1,mod=1e9+7;
  11. int h,w,m,n,map[N][N];
  12. struct Node{
  13. int a,b,x,y,v,sum;
  14. bool operator<(const Node &gg)const
  15. {return v==gg.v?sum<gg.sum:v<gg.v;}
  16. }q[11];
  17. inline int ksm(long long x,int b)
  18. {
  19. long long res=1;
  20. for(;b;b>>=1,x=x*x%mod)
  21. if(b&1)
  22. res=res*x%mod;
  23. return res;
  24. }
  25. int main()
  26. {
  27. freopen("grid.in","r",stdin);
  28. freopen("grid.out","w",stdout);
  29. h=read(),w=read(),m=read(),n=read();
  30. int sum=h*w;
  31. for(int i=1;i<=n;++i)
  32. {
  33. q[i]=(Node){read(),read(),read(),read(),read(),0};
  34. q[i].sum=(q[i].x-q[i].a+1)*(q[i].y-q[i].b+1);
  35. }
  36. std::sort(q+1,q+1+n);
  37. for(int v,k=1;k<=n;++k)
  38. {
  39. v=q[k].v;
  40. for(int i=q[k].a;i<=q[k].x;++i)
  41. {
  42. for(int j=q[k].b;j<=q[k].y;++j)
  43. {
  44. if(map[i][j]>v || map[i][j]==0)
  45. map[i][j]=v,--sum;
  46. else --q[k].sum;
  47. }
  48. }
  49. }
  50. long long emp,ans=ksm(m,sum);
  51. for(int i=1;i<=n;++i)
  52. {
  53. if(!q[i].sum)continue;
  54. emp=(ksm(q[i].v,q[i].sum)-ksm(q[i].v-1,q[i].sum)+mod)%mod;
  55. ans=ans*emp%mod;
  56. }
  57. printf("%d",ans);
  58. fclose(stdin);fclose(stdout);
  59. return 0;
  60. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注