题目传送https://www.luogu.org/problem/show?pid=1090
这道水题有很多种做法是显然的,,,,在这里的就介绍一下我的几种做法吧
法一:
先排序,取两个最小的相加加入数组,再排序。。。
(这种做法谁都想得到吧,,,然后显然超时)
法二:
事先不是已经排过一遍序了吗?
那我把合并好了的那个数插入到数组中合适的位置不就ok了吗?
代码:
#include
#define r(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int a[10001],n,i,t,ans;
int main()
{
scanf("%d",&n);
r(i,1,n) scanf("%d",&a[i]);
sort(a+1,a+n+1);
r(i,1,n-1)
{
a[i+1]+=a[i];
ans+=a[i+1];
t=i+1;
while(ta[t+1])
swap(a[t],a[t+1]),t++;
}
printf("%d",ans);
return 0;
}
这个做法虽然不会超时(对于洛谷的数据,最大的300ms左右),但是效率还是比较低的。
法三:
想必优先队列都知道吧(不会的点这里http://blog.csdn.net/morewindows/article/details/6976468)
既然知道优先队列岂不是很简单了?
从优先队列里拿出最小的两个,然后合并再插入队列。
因为本人比较懒,所以就没写什么priority_queue
#include
#include
using namespace std;
priority_queueq;
int n,i,a,b,x,ans;
int main()
{
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&x),q.push(-x);
while(q.size()>1){
a=q.top();q.pop();
b=q.top();q.pop();
ans+=a+b;
q.push(a+b);
}
return !printf("%d",-ans);
}
法四:
堆。其实和上一种做法差不多,弄个小根堆就可以了。
堆的话不想自己写就用 stl的heap吧(不会的点这里http://blog.csdn.net/morewindows/article/details/6967409)
法五(其实的法二是改进版):
贪心:
每次取出两个最小的合并 后再变为一个再插入递增序列里
由于一直sort会超时
所以直接尝试了一下插入排序
#include
#include
#include
int n;
int Ft[10000];
int Fte; //当前序列里数的个数
int Out;
void Input() //输入及初始化
{
scanf("%d",&n);
int wi;
for(wi=0;wi
法六(借鉴的一位神犇的):
快排+单调队列
为了快速&简洁,所以快排直接调用std::sort。假设数据用数组a存储,再新建数组b。
分别用两个指针指向两个队列的队首和队尾(p,q指向a;r,s指向b)。由于a,b都是单调队列,所以最少的两堆果子有3种情况:1.a[p]和a[p+1];2.a[p]和b[r];3.b[r]和b[r+1]
合并它们,并插到b的队尾,循环做n-1次即可。
时间复杂度:快排O(nlogn),单调队列O(n),加起来还是[color=red]O(nlogn)[/color]
空间复杂度:需要O(n)的辅助空间
代码复杂度:较低,只有0.6K
实测0ms秒过,瞬间rank1
#include
#include
int a[10010],b[10010],n,t,p,q,r,s,k;
long long ans;
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
ans=0;
std::sort(a+1,a+n+1);
p=1;
q=n;
r=1;
s=0;
for (int i=1;ip){
t=a[p]+a[p+1];
k=1;
}
if (q>=p && s>=r && a[p]+b[r]r && b[r]+b[r+1]
法七(同样也是借鉴一位神犇的):
#include
#include
#include
#include
using namespace std;
int number[20000+1];//存放下标对应值的数的个数
int BigNumber[15000+1];//顺序存放大于2万的群数据
int n;//为人数
int power;//消耗体力值
void solve()
{
int i,p,q;
long total=0;
p=1;//存放number数组没合并前的最小指针数
q=1;//存放BigNumber数组没合并前的最小指针数
i=0;//存放每次合并时已合并的群数
while(n>1)//如还没合并完,则继续合并
{
if(p<=20000)//如有个数小于2万的群没有合并
{
if(number[p]>0)//如果number下标对应群存在,则合并
{
i++;
total+=p;
number[p]--;
}
else
p++;//没有个数为p的值则往下找
}
else//所有群的个数都大于2万,则顺序从number数据中取出群合并
{
i++;
total+=BigNumber[q];
q++;
}
if((total<=20000)&&(i==2))//一次合并后(i=2),且合并后的个数不超过2万
{
number[total]++;//将合并的值个数放入小数组中
n--;
i=0;
power+=total;
total=0;
}
else
if(i==2)//一次合并完,且合并后的个数大于2万
{
BigNumber[0]++;//哨兵,表示下一次取值从大数组的何位置取
BigNumber[BigNumber[0]]=total;//存入大数组
n--;
power+=total;
i=0;
total=0;
}
}
printf("%d\n",power);
}
void init()
{
int i,k;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&k);
number[k]++;
}
}
int main()
{
power=0;
init();//处理输入数据
solve();
return 0;
}
先排序a数组,然后在开一个数组b
于是乎就有3个指针了(a数组指针"i",b数组指针"j"和b数组大小的指针"m")
把合并好了的放到b数组的最后(显然b是单调的)
每次取MIN=min{a[i]+a[i+1],b[j]+b[j+1],a[i]+b[j]}
然后ans+=MIN,然后把这个MIN插入到b的末尾,然后指针往后移;
#include
#include
#define aa(x) a[x]+a[x+1]
#define ab(x,y) a[x]+b[y]
#define bb(x) b[x]+b[x+1]
const int N=12017,max=1<<29;
int n,m,i,j,ans,Min,a[N],b[N];
inline int min(int x,int y){return x
我觉得我这个想法还是挺不错的
总结
其实这个题目本身很水,但他却有这么多做法(耗时是递减的)。
其实对于一道题,我们不是会一种做法就ok了,而是应在时间允许的范围内,去钻研最优解,钻研有没有更好的做法,而不是知道一种就够了。
此余之所得矣。