[关闭]
@hhzhhzhhz 2019-08-22T08:39:56.000000Z 字数 5257 阅读 329

暑期模拟赛九 解题报告


T1:瞬间移动

  有一天,暮光闪闪突然对如何将一个整数序列a1,a2,...,an排序为一个不下降序列起了兴趣。身为一只年轻独角兽的她,只能进行一种叫做“单元转换”(unit shift)的操作。换句话说,她可以将序列的最后一个元素移动到它的起始位置:


  请帮助暮光闪闪计算一下:她对这个序列进行排序所需的最小操作次数是多少?

测试数据:

测试输入1 测试输入2 测试输入3
2
2 1
3
1 3 2
2
1 2
测试输出1 测试输出2 测试输出3
1 -1 0

算法分析:


  • 这里提供两种算法当然还有更多

    • (大多数同学):
      对于能够变成不下降序列的序列一定满足这样的特性:

      1. 有两段不下降序列组成(第二段可为空串)
      2. 第二段的末尾元素<=第一段的首元素


       若都符合则答案为第二串长度。

    • (缺根筋的本人):
      对于能够变成不下降序列的序列一定满足这样的特性:
      1 . 将这样的序列连成环,在最大的序列后一位开始遍历环,必是一段不下降序列。 答案就是

【AC代码】

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int a[100010],s[100010],n[100010];
  4. int main() {
  5. int n,t=-1,mi=0;
  6. scanf("%d",&n);
  7. for(int i=0; i<n; i++) {
  8. scanf("%d",&a[i]);
  9. if(a[i]>=t) {
  10. t=a[i];
  11. mi=i;
  12. }
  13. s[i]=a[i];
  14. }
  15. int t1=1;
  16. sort(s,s+n);
  17. for(int i=1,j=mi+2; i<n; i++,j++) {
  18. if(j<n)
  19. if(s[i]!=a[j]) {
  20. t1=-1;
  21. break;
  22. } else if(s[i]!=a[j%n]) {
  23. t1=-1;
  24. break;
  25. }
  26. }
  27. if(t1==-1 ) cout<<-1;
  28. else cout<<n-mi-1;
  29. return 0;
  30. }

T2:食物订购

  新年快到了,乐乐开了一家著名的餐厅,名叫“巧克力餐厅。
这个餐厅可提供n种菜,其中第i种菜的单价为ci,共制作了ai份。从餐厅订单上显示,今天会有m个客户将一个接一个光临餐厅,第j个客户将购买第ti种食物dj份,并且第j+1个客户只有在第j个客户购买以后才会到来。如果餐厅无法满足这个人对第ti种菜的需求的话,那他就会买目前还有的价格最低的菜来满足他的需求,最低的菜买好后还不够他会继续买目前最低的菜,直到满足他的需求。(提示:如果所有菜都卖完了,但还是不能满足他的需求,那么就直接输出0,因为餐厅不能满足这个人的需要求,客户会愤怒地离开,无论之前提供多少菜肴,这个客户都不会买单,吃霸王餐)。如果餐厅能满足这个人的需求,就输出这个人的总消费数。
  总之每个人必须先买他想要的那种菜,数量不够时再买价格最低的,直到买到他所需的数量为止,当然将所有菜都买完也不能满足他的需求,则输出0。你的任务是计算每个人的总消费数。

测试数据:

in.1 in.2 in.3
8 5
8 6 2 1 4 5 7 5
6 3 3 2 6 2 3 2
2 8
1 4
4 7
3 4
6 10
6 6
6 6 6 6 6 6
6 66 666 6666 66666 666666
1 6
2 6
3 6
4 6
5 6
6 66
6 6
6 6 6 6 6 6
6 66 666 6666 66666 666666
1 6
2 13
3 6
4 11
5 6
6 6
out.1 out.2 out.3
22
24
14
10
39
36
396
3996
39996
399996
0
36
11058
99996
4333326
0
0

算法分析:
巨大的模拟题,注意每个变量所表示的含义,不要混乱。

【AC代码】

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. long long n,m,cnt;//cnt 总共还有多少菜
  4. struct forever {
  5. long long c,num,nxt,id;//c价格,num数量,nxt下一个最小的菜,id 序号
  6. } a[100010];
  7. bool cmp1(forever x,forever y) {
  8. return x.c<y.c;
  9. }
  10. bool cmp2(forever x,forever y) {
  11. return x.id<y.id;
  12. }
  13. int main() {
  14. scanf("%lld%lld",&n,&m);
  15. for(int i=1; i<=n; i++) {
  16. scanf("%lld",&a[i].num);
  17. a[i].id=i;
  18. a[i].nxt=1e9;
  19. cnt+=a[i].num;
  20. }
  21. for(int i=1; i<=n; i++) {
  22. scanf("%lld",&a[i].c);
  23. }
  24. sort(a+1,a+1+n,cmp1);
  25. for(int i=1; i<n; i++) {
  26. a[i].nxt=a[i+1].id;
  27. }
  28. int p=a[1].id;
  29. sort(a+1,a+1+n,cmp2);
  30. while(m--) {
  31. long long iid,inum,ans=0;
  32. scanf("%lld%lld",&iid,&inum);
  33. if(cnt<inum) {
  34. cnt=0;
  35. printf("0\n");
  36. continue;
  37. }
  38. cnt-=inum;
  39. if(a[iid].num>=inum) {
  40. a[iid].num-=inum;
  41. ans=a[iid].c*inum;
  42. } else {
  43. ans+=a[iid].num*a[iid].c;
  44. inum-=a[iid].num;
  45. a[iid].num=0;
  46. while(a[p].num<inum) {
  47. inum-=a[p].num;
  48. ans+=a[p].c*a[p].num;
  49. a[p].num=0;
  50. p=a[p].nxt;
  51. }
  52. ans+=inum*a[p].c;
  53. a[p].num-=inum;
  54. }
  55. printf("%lld\n",ans);
  56. while(p!=1e9 &&a[p].num==0)p=a[p].nxt;
  57. }
  58. return 0;
  59. }

T3:马蹄印

   虽然当奶牛贝里斯找到平衡序列后很高兴了,但是他现在对序列提出了一个更高的要求,就是要求每个序列中必须是先一定数量的左括号然后是与左括号相同数量的右括号。例如:(((()))),就是一个完美的平衡序列。
  当贝里斯某天在农场上走的时候,他在地上发现了马蹄印,这个农场是一个N*N的方格,每个小方格中都有一个马蹄印。贝里斯希望从方格的最左上角的地方开始出发,然后每次可以向上或者向下或者向左或者向右移动一步,使得他走过的每个小方格中的马蹄印能够组成一个完美的平衡序列。当然了,贝里斯不能重复经过任何小方格。
  请帮助贝里斯在这个N*N的方格中找出长度最长的完美序列的长度。

测试数据:

in out
4
(())
()((
(()(
))))
8

【算法分析】
dfs,当然略显奇葩的本人也有不同的做法;

1.(一般做法)
dfs 参数中记录,前一个状态是左括号或右括号,跑完地图;
2.(本人奇葩做法)
跑两个dfs;第一个走通所有的与原点连通的左括号;第二个在所有的左括号节点向四面查找右括号的路径,当这条路径上的左扩号与右括号数相同时记录答案;

以上做法都注意要记录路径+回溯

【AC代码】

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int ans=0;
  4. int n;
  5. char c[6][6];
  6. bool fg1[6][6],fg2[6][6];//fg1记录左,fg2记录右
  7. void fa(int l,int x,int y,int r) {
  8. if(r==l) {
  9. ans=max(ans,2*r);
  10. return ;
  11. }
  12. //蒟蒻本体,对于滚动数组来处理路径不是很熟练,于是复制粘贴贴4份解决。emm
  13. if(x+1<=n &&fg2[x+1][y]==0 &&c[x+1][y]==')') {
  14. fg2[x+1][y]=1;
  15. fa(l,x+1,y,r+1);
  16. fg2[x+1][y]=0;
  17. }
  18. if(y+1<=n &&fg2[x][y+1]==0 &&c[x][y+1]==')') {
  19. fg2[x][y+1]=1;
  20. fa(l,x,y+1,r+1);
  21. fg2[x][y+1]=0;
  22. }
  23. if(y-1>=1 &&fg2[x][y-1]==0 &&c[x][y-1]==')') {
  24. fg2[x][y-1]=1;
  25. fa(l,x,y-1,r+1);
  26. fg2[x][y-1]=0;
  27. }
  28. if(x-1>=1 &&fg2[x-1][y]==0 &&c[x-1][y]==')') {
  29. fg2[x-1][y]=1;
  30. fa(l,x-1,y,r+1);
  31. fg2[x-1][y]=0;
  32. }
  33. return;
  34. }
  35. void dfs(int l,int x,int y) {
  36. if(x+1<=n &&fg1[x+1][y]==0) {
  37. if(c[x+1][y]=='(') {
  38. fg1[x+1][y]=1;
  39. dfs(l+1,x+1,y);
  40. fg1[x+1][y]=0;
  41. } else {
  42. fg2[x+1][y]=1;
  43. fa(l,x+1,y,1);
  44. fg2[x+1][y]=0;
  45. }
  46. }
  47. if(y+1<=n &&fg1[x][y+1]==0) {
  48. if(c[x][y+1]=='(') {
  49. fg1[x][y+1]=1;
  50. dfs(l+1,x,y+1);
  51. fg1[x][y+1]=0;
  52. } else {
  53. fg2[x][y+1]=1;
  54. fa(l,x,y+1,1);
  55. fg2[x][y+1]=0;
  56. }
  57. }
  58. if(y-1>=1 &&fg1[x][y-1]==0) {
  59. if(c[x][y-1]=='(') {
  60. fg1[x][y-1]=1;
  61. dfs(l+1,x,y-1);
  62. fg1[x][y-1]=0;
  63. } else {
  64. fg2[x][y-1]=1;
  65. fa(l,x,y-1,1);
  66. fg2[x][y-1]=0;
  67. }
  68. }
  69. if(x-1>=1 &&fg1[x-1][y]==0) {
  70. if(c[x-1][y]=='(') {
  71. fg1[x-1][y]=1;
  72. dfs(l+1,x-1,y);
  73. fg1[x-1][y]=0;
  74. } else {
  75. fg2[x-1][y]=1;
  76. fa(l,x-1,y,1);
  77. fg2[x-1][y]=0;
  78. }
  79. }
  80. return ;
  81. }
  82. int main() {
  83. cin>>n;
  84. for(int i=1; i<=n; i++)
  85. for(int j=1; j<=n; j++)
  86. cin>>c[i][j];
  87. fg1[1][1]=true;
  88. if(c[1][1]==')') {//特判处理第一步就是右括号的情况,这样不能形成(平衡序列)
  89. cout<<0;
  90. return 0;
  91. }
  92. dfs(1,1,1);//(,),x,y;
  93. cout<<ans;
  94. }

T4:景观美化

  农夫约翰最近决定来美化他的花园,他需要运输很多的泥土。花园是由N块花圃组成的。第i块花圃初始的时候有Ai数量的泥土。为了达到美化的目的,必须使得第i块花圃的泥土数量Ai变成Bi。
  约翰有三个选择:第一,他可以买一个单位的泥土放进任意花圃中,代价是X;第二,他可以将一个单位的泥土从某一个花圃中除去,代价是Y;第三,他可以将第i块花圃中的一个单位的泥土搬运到第j块花圃中,代价是Z*|i-j|
  请帮助约翰计算为了达到目的最小需要花费的代价。

测试数据:

in out
4 100 200 1
1 4
2 3
3 2
4 0
210

算法分析:
对于这样的输入数据难以线性处理;
于是将各式进行转化:
原始(序列a): 1 2 3 4 ----> 1 2 2 3 3 3 4 4 4 4
目标(序列b): 4 3 2 0 ----> 1 1 1 1 2 2 2 3 3
这样这个题目就变成要求将 a变成b的最小代价;
也就能将它转化为多个子问题求最优解。
即 将a的第1~i位变为b的第1~j位的最小代价;
故本题为DP;
动态转移方程:

【AC代码】

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int a[1010],b[1010],n,x,y,z;
  4. int f[1010][1010];
  5. int main() {
  6. scanf("%d %d %d %d",&n,&x,&y,&z);
  7. int t1,t2,tot1=0,tot2=0;
  8. for(int i=1; i<=n; i++) {
  9. scanf("%d %d",&t1,&t2);
  10. while(t1--) a[++tot1]=i;
  11. while(t2--) b[++tot2]=i; //格式转换
  12. }
  13. //预处理
  14. for(int i=0; i<=tot1; i++)
  15. f[i][0]=y*i; //每一块都挖走
  16. for(int i=0; i<=tot2; i++)
  17. f[0][i]=x*i; //每一块都买土
  18. //begin
  19. for(int i=1; i<=tot1; i++)
  20. for(int j=1; j<=tot2; j++)
  21. f[i][j]=min(f[i-1][j]+y,min(f[i][j-1]+x,f[i-1][j-1]+z*abs(a[i]-b[j])));
  22. cout<<f[tot1][tot2];
  23. return 0;
  24. }

end

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注