鱼C论坛

 找回密码
 立即注册
查看: 2188|回复: 4

[已解决]我写的这个程序怎么过不了啊?

[复制链接]
发表于 2021-6-14 12:41:08 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
  1. #include<iostream>
  2. #include<cstdio>
  3. using namespace std;

  4. int e,n,maxx=0,a[101][6]={};

  5. void s(int i)
  6. {
  7.         int mm=0;
  8.         int x=i+1;
  9.         if(x>n)
  10.         x=1;
  11.         for(int j=1;j<n;j++)
  12.         {
  13.                 mm=mm+a[i][1]*a[x][1]*a[x][2];
  14.                 cout<<"首"<<a[i][1]<<" 中"<<a[x][1]<<" 尾"<<a[x][2]<<" x"<<x<<endl;
  15.                 x=a[x][3];
  16.         }
  17.         cout<<mm<<"  "<<endl;
  18.         cout<<endl;
  19.         if(maxx<mm)
  20.                 maxx=mm;
  21. }

  22. void ss(int i)
  23. {
  24.                 int mm=0;
  25.         int x=i;
  26.        
  27.         for(int j=1;j<n;j++)
  28.         {
  29.                 if(x==0)
  30.                 x=n;
  31.                 mm=mm+a[i][1]*a[a[x][4]][1]*a[a[a[x][4]][4]][1];
  32.                 cout<<"首"<<a[i][1]<<" 中"<<a[a[x][4]][1]<<" 尾"<<a[a[a[x][4]][4]][1]<<" x"<<x<<endl;
  33.                 x=a[x][4];
  34.         }
  35.         cout<<mm<<"  "<<endl;
  36.         cout<<endl;
  37.         if(maxx<mm)
  38.                 maxx=mm;
  39. }

  40. int main()
  41. {
  42.         freopen("in.txt","r",stdin);
  43.         cin>>n;
  44.         for(int i=1;i<=n;i++)
  45.         {
  46.                 cin>>a[i][1];
  47.         }
  48.         a[n+1][1]=a[1][1];
  49.         for(int i=1;i<=n;i++)
  50.         {
  51.                 a[i][2]=a[i+1][1];
  52.                 a[i][3]=i+1;
  53.                 a[i][4]=i-1;
  54.                 a[i][5]=i;
  55.         }
  56.         a[n][3]=1;
  57.         a[n+1][2]=a[1][2];
  58.         a[1][4]=n;
  59.         for(int i=1;i<=n;i++)
  60.         {
  61.                 s(i);
  62.                 ss(i);
  63.         }
  64.         cout<<maxx;
  65. }
复制代码

来源————一本通c++
1843:【06NOIP提高组】能量项链

时间限制: 1000 ms         内存限制: 65536 KB
提交数: 535     通过数: 322
【题目描述】
在Mars星球上,每个Mars人都随身佩带着一串能量项链。在项链上有N颗能量珠。能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量。如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为:m*r*n(Mars单位),新产生的珠子的头标记为m,尾标记为n。

需要时,Mars人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不同的,请你设计一个聚合顺序,使一串项链释放出的总能量最大。

例如:设N=4,4颗珠子的头标记与尾标记依次为(2,3) (3,5) (5,10) (10,2)。我们用记号⊕表示两颗珠子的聚合操作,(j⊕k)表示第j,k两颗珠子聚合后所释放的能量。则第4、1两颗珠子聚合后释放的能量为:

(4⊕1)=10*2*3=60。

这一串项链可以得到最优值的一个聚合顺序所释放的总能量为

((4⊕1)⊕2)⊕3)=10*2*3+10*3*5+10*5*10=710。

【输入】
第一行是一个正整数N(4≤N≤100),表示项链上珠子的个数。第二行是N个用空格隔开的正整数,所有的数均不超过1000。第i个数为第i颗珠子的头标记(1≤i≤N),当i<N时,第i颗珠子的尾标记应该等于第i+1颗珠子的头标记。第N颗珠子的尾标记应该等于第1颗珠子的头标记。

至于珠子的顺序,你可以这样确定:将项链放到桌面上,不要出现交叉,随意指定第一颗珠子,然后按顺时针方向确定其他珠子的顺序。

【输出】
只有一行,是一个正整数E(E≤2.1*109),为一个最优聚合顺序所释放的总能量。

【输入样例】
4
2  3  5  10
【输出样例】
710
最佳答案
2021-6-14 22:34:47
本帖最后由 yuxijian2020 于 2021-6-15 17:21 编辑

这种题目,一般都有时间,空间要求
而且不是很难的话  对时间、空间要求就更严
所以不要抬手就写,分析一下

首先,聚合能量的公式 是  前首 * 前尾 * 后尾(或者说 前首 * 后首 * 后尾 )
那么,题目中要求的 总能量最大 是要满足什么条件呢?

看看例子, 2 3 5 10
是从最后一个珠子开始聚合,为什么?
因为 10 是最大的数!
要保证能量最大,就要保证最大的数要从头乘到尾
那怎么总是保留最大的数呢?
最大的数要总是处于头的位置!

所以,此时再看题目就很简单的
说白了就是找到最大的数  然后按照公式计算结果输出就可以了

所以,答案
  1. #include <iostream>

  2. using namespace std;

  3. int PowerCount(int* beads, int count)
  4. {
  5.     int pos      = 0;       //最大的数在数组中的位置开始往后
  6.     int maxNum   = beads[0];//最大的数
  7.     int next     = 0;       //pos的下个位置
  8.     int result   = 0;       //结果
  9.     int i        = 0;       //临时变量

  10.     //首先找到最大的数在数组中的位置 -- 从第2个数开始
  11.     for (i = 1; i < count; ++i)
  12.     {
  13.         if(beads[i] > beads[i - 1])
  14.         {
  15.             pos = i;
  16.             maxNum = beads[i];
  17.         }
  18.     }

  19.     //然后按公式求和 -- 从最大的数开始往后
  20.    
  21.     i = 1;      //计算次数为 数组个数 - 1

  22.     do
  23.     {
  24.         pos += 1;
  25.         if(pos >= count)
  26.             pos = 0;

  27.         next = pos + 1;
  28.         if(next >= count)
  29.             next = 0;

  30.         result += maxNum * beads[pos] * beads[next];

  31.         ++i;
  32.     } while (i < count);    //这里 i 只用于记录次数

  33.     return result;
  34. }

  35. int main()
  36. {
  37.     int beads[] = { 2, 3, 5, 10 };

  38.     cout << PowerCount(beads, 4) << endl;

  39.     return 0;
  40. }
复制代码


时间复杂度 O(n) -- 遍历2次数组
空间复杂度 O(1) -- 常数个数的变量

111.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2021-6-14 21:52:56 | 显示全部楼层
C++我的痛苦
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-6-14 22:34:47 | 显示全部楼层    本楼为最佳答案   
本帖最后由 yuxijian2020 于 2021-6-15 17:21 编辑

这种题目,一般都有时间,空间要求
而且不是很难的话  对时间、空间要求就更严
所以不要抬手就写,分析一下

首先,聚合能量的公式 是  前首 * 前尾 * 后尾(或者说 前首 * 后首 * 后尾 )
那么,题目中要求的 总能量最大 是要满足什么条件呢?

看看例子, 2 3 5 10
是从最后一个珠子开始聚合,为什么?
因为 10 是最大的数!
要保证能量最大,就要保证最大的数要从头乘到尾
那怎么总是保留最大的数呢?
最大的数要总是处于头的位置!

所以,此时再看题目就很简单的
说白了就是找到最大的数  然后按照公式计算结果输出就可以了

所以,答案
  1. #include <iostream>

  2. using namespace std;

  3. int PowerCount(int* beads, int count)
  4. {
  5.     int pos      = 0;       //最大的数在数组中的位置开始往后
  6.     int maxNum   = beads[0];//最大的数
  7.     int next     = 0;       //pos的下个位置
  8.     int result   = 0;       //结果
  9.     int i        = 0;       //临时变量

  10.     //首先找到最大的数在数组中的位置 -- 从第2个数开始
  11.     for (i = 1; i < count; ++i)
  12.     {
  13.         if(beads[i] > beads[i - 1])
  14.         {
  15.             pos = i;
  16.             maxNum = beads[i];
  17.         }
  18.     }

  19.     //然后按公式求和 -- 从最大的数开始往后
  20.    
  21.     i = 1;      //计算次数为 数组个数 - 1

  22.     do
  23.     {
  24.         pos += 1;
  25.         if(pos >= count)
  26.             pos = 0;

  27.         next = pos + 1;
  28.         if(next >= count)
  29.             next = 0;

  30.         result += maxNum * beads[pos] * beads[next];

  31.         ++i;
  32.     } while (i < count);    //这里 i 只用于记录次数

  33.     return result;
  34. }

  35. int main()
  36. {
  37.     int beads[] = { 2, 3, 5, 10 };

  38.     cout << PowerCount(beads, 4) << endl;

  39.     return 0;
  40. }
复制代码


时间复杂度 O(n) -- 遍历2次数组
空间复杂度 O(1) -- 常数个数的变量

111.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-6-20 09:26:23 | 显示全部楼层
最佳答案过不了
记忆化递归搜索方式解决:根据题意,首先要清楚,本题无法通过贪心的方式解答。
最后一次合并,两颗珠子合并成一颗珠子,共n种情况,珠子呈环状结构。即第1种情况,从1号珠子开始,连续的n个珠子合并,第2种情况,从2号珠子开始,连续的n个珠子合并,。。。。。,第n种情况,从n号珠子开始,连续n个珠子合并。最终需要从这n种情况中选择最大总能量。因为能量珠连成环,处理不方便,所以把它展开成链。把原先的n个能量珠扩成2n-1个,第i个和第i+n个相同。总能量ans可以表示成
ans=max(f(1,n),f(2,n+1),f(3,n+2),…….f(n,2n-1)),其中f(1,n)表示从1号珠子开始到n号珠子结束,能获得的最大能量。
对于f(1,n),是最终由两颗珠子合并而成,若干种情况,如,1,2--n;1--2,3--n;。。。。。。1--n-1,n,从这若干种情况中选择能量最大的
求解过程可以表示成
Ans=max(f(1,1)+f(2,n)+ head[1]*tail[1]*tail[n], f(1,2)+f(3,n)+ head[1]*tail[2]*tail[n],。。。。。,f(1,n-1)+f(n,n)+ head[1]*tail[n-1]*tail[n])

对于f(1,1),f(2,n) 。。。。。。。f(1,n-1),f(n,n),同理可求
由此可见,可以通过递归搜索的方式解决问题。
f(i,j)
{
ans=0;
for(int k=i;k<j;k++)
  ans=max(ans,f(i,k)+f(k+1,j)+ head*tail[k]*tail[j])
}
边界条件 f(k,k)=0
通过简单递归搜索,很可能超时,仔细观察发现,里面有很多小段的珠子能量
会重复计算合并,本题需采用记忆化递归搜索。


动态规划方式解决:
对于区间[i,j]合并成的能量珠,它一定是由两个能量珠合并而来,我们假设它是由[i,k]和[k+1,j]合并成的。

记dp[j]为合并成[i,j]获得的最大能量,我们枚举断点k,那么我们很容易得出状态转移方程

dp[j]=max(dp[k]+dp[k+1][j]+head*tail[k]*tail[j]),其中head为头标记,tail为尾标记。

因为能量珠连成环,处理不方便,所以把它展开成链。

我们把原先的n个能量珠扩成2n-1个,第i个和第i+n个相同,我们只需要进行DP后找出max(dp[i+n-1])就是答案。

枚举合并的次数t,枚举i,那么j就是i+n,枚举k,进行转移即可。

时间复杂度O(n^3)。

  1. [code]#include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. #include<cstring>
  5. #include<cmath>
  6. #define nn 2*n-1
  7. using namespace std;
  8. int dp[500][500],head[500],tail[500],n,ans;
  9. int main()
  10. {
  11.         cin>>n;
  12.         for(int i=1;i<=n;i++){
  13.                 scanf("%d",&head[i]);
  14.                 head[n+i]=head[i];
  15.         }
  16.         for(int i=1;i<=nn;i++) tail[i]=head[i%n+1];
  17.         for(int i=1;i<=nn;i++) dp[i][i]=0;
  18.         for(int t=1;t<n;t++){
  19.                 for(int i=1;i<=nn;i++){
  20.                         int j=i+t;
  21.                         for(int k=i;k<j;k++) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+head[i]*tail[k]*tail[j]);
  22.                 }
  23.         }
  24.         for(int i=1;i<=nn;i++) ans=max(ans,dp[i][i+n-1]);
  25.         cout<<ans;
  26.        
  27.         return 0;
  28. }
复制代码

[/code]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-6-20 13:03:53 | 显示全部楼层
  1. for(int i=n-1;i>=1;i--)
  2.         {
  3.                 for(int j=i+1;j<=n;j++)
  4.                 {
  5.                         for(int k=i;k<=j-1;k++)
  6.                         {
  7.                                 f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
  8. //                                cout<<f[i][j]<<"  ";
  9.                         }
  10.                 }
  11.         }
复制代码

解释一下,i就不说了,j是从i开始的可以到达的一个末尾,k是在ij直接切开一段进行计算,s数组是前n项的和
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-8 09:51

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表