合并果子
合并果子
| Time Limit: 1000MS | Memory Limit: 65535KB |
| Submissions: 4092 | Accepted: 466 |
Sample Input
3 1 2 9
Sample Output
15
分析:
大致题意:就是每次将数目最少的两种果子加起来形成新的一种,并同时累计每次的和
思路分析:
【解法一】
并不是每次所得和的值都是最小的,所以有以下想法:①加一次排一次序,时间复杂度为O(n^3)②那排序花的时间多,就优化排序吧,其实只是一个数的变动,用插入吧,在多想一点折半查找,时间复杂度也是O(n^2*logn),显然还是行不通
这里想到用hash就对了,输入一个数num,就sign[num]++,每次从1开始往后取,就减少了排序过程;再者,由于一开始所有数据,都在20000内,那就定sign[40000]吧,为什么呢?后面的20000是加的次数,如果也将和sum作sign[sum]++处理的话,以题上的数据量,和最大可以达到28000000,开这么大的数组肯定不可能,那这里有个很不错的方法:就是将数组分开用,前20000---sign[num]++,超过20000的---sign[pos++]=sum(pos初始值为20001)。
就看代码吧
# include<stdio.h> # include<string.h> #define N 20000 int now; int sign[2*N]; int solve(int sign[]) { while(sign[now]==0) now++; if(now<=N) { sign[now]--;//用了一个就减去一个 return now; } else return sign[now++];//如果now找到了20000以上,返回sign[now] } int main() { int n,i; int num,s1,s2,sum=0; int pos=N+1; scanf("%d",&n); memset(sign,0,sizeof(sign)); for(i=0;i<n;i++) { scanf("%d",&num); sign[num]++; } now=1; for(i=0;i<n-1;i++) { s1=solve(sign); s2=solve(sign); sum+=s1+s2; if(s1+s2<=N) { sign[s1+s2]++; } else { sign[pos++]=s1+s2;//如果now找到了20000以上,就换成这种方法来存 } } printf("%d\n",sum); return 0; }
【解法二】
其实就是贪心算法,但还是上面说的,没有合适的调整算法,肯定会超时;这里想到一种,虽然没第一种方法节省时间,但是很明显的贪心,就是用优先队列
代码如下:
# include<iostream> # include<queue> # include<algorithm> using namespace std; int main() { int sign[10005]; int n,temp,sum=0; int i; priority_queue<int,vector<int>,greater<int> > q; cin>>n; for(i=0;i<n;i++) { cin>>sign[i]; q.push(sign[i]); } while(!q.empty()) { temp=q.top(); q.pop(); if(!q.empty()) { temp+=q.top(); q.pop(); } sum+=temp; if(q.empty()) break; q.push(temp); } cout<<sum<<endl; return 0; }
注:这道题是求最小消耗值,所以最小两个加起来不一定是最小的,需要再调整顺序;但如果是求最大的消耗值的话,最大两个加起来一定是最大的,就可用以下代码,时间复杂度为O(n)
# include<stdio.h> # include<algorithm> using namespace std; int main() { int sign[10005]; int n,sum=0; int i; scanf("%d",&n); for(i=0;i<n;i++) scanf("%d",&sign[i]); sort(sign,sign+n); for(i=n-1;i>=0;i--) sum+=(i+1)*sign[i]; if(n>=1) sum-=sign[n-1]; printf("%d\n",sum); return 0; }
浙公网安备 33010602011771号