【学术篇】一石三鸟的——洛谷2654——原核生物培养(石子合并果子)
2017-03-22 10:28
344 查看
传送门:https://www.luogu.org/problem/show?pid=2654
精简版题意:对n个数做k次操作,每次取最小的m个放在指定环形位置,进行m-1次合并,每次将相邻的两个数花费两数之和的代价将他们合并为两数之和,求最小代价。。
题目分析:用石子合并的方式合并果子。。(果子合并)
之间用纯模拟就好了。。(数据范围辣么小,怎么乱搞都能过啊。。)
至于合并果子?当时还是用来练手写堆的。。而现在我直接懒到直接priority_queue了。。
(反正省选 noi都开O2嘛2333)
此题做法:将所有数据压入堆中(堆排序,但有dalao用了基数排序虐场orz),根据贪心原则,每次从堆顶弹出两个最小的果子合并,再压入堆即可。
此题不偷懒的手写堆做法(年轻的我写的,连个快读都没有):
偷懒的priority_queue做法(加了快读都比上面短):
至于石子合并?我刚开始是拒绝的。。其实是我发现自己不会写了 后来想了一会,好像有辣么一点印象。。然后循环的结果。。我是崩溃的。。里面的循环傻傻分不清了喂。。
于是我又去做了一遍。。
这题的原理:经典的环形dp
首先一看环形dp,先将数组扩到2n..
然后状态转移方程大概就是:
其中的sigma可以通过前缀和。。
然后枚举i,j,k就好了(我习惯枚举长度和i来推j..看个人喜好吧。。)
石子合并代码(这里要求最小值和最大值,应该是f最小值,g最大值)
然后就是我的a数组直接原地求前缀和了。。看上去有点方,但是能看。。
好的。。扯了这么长时间= =
能看到这里的都是真爱吧(什么鬼)
上面的原理搞清楚了,
然后中间就用循环乱搞即可。。
(话说我第一遍还看错题了什么鬼)
本题的代码(其实是上面的合并,但是我重构啦♪(^∇^*)
嗯 就是这样。。
完成了一次传说中的三合一。。
这三个题难度保证递增,然而会了前两个怎么都能拼出第三个嘛。。
然而我几乎忘了石子合并怎么做( ⊙o⊙ )~~
唉我还是太弱了
精简版题意:对n个数做k次操作,每次取最小的m个放在指定环形位置,进行m-1次合并,每次将相邻的两个数花费两数之和的代价将他们合并为两数之和,求最小代价。。
题目分析:用石子合并的方式合并果子。。(果子合并)
之间用纯模拟就好了。。(数据范围辣么小,怎么乱搞都能过啊。。)
至于合并果子?当时还是用来练手写堆的。。而现在我直接懒到直接priority_queue了。。
(反正省选 noi都开O2嘛2333)
此题做法:将所有数据压入堆中(堆排序,但有dalao用了基数排序虐场orz),根据贪心原则,每次从堆顶弹出两个最小的果子合并,再压入堆即可。
此题不偷懒的手写堆做法(年轻的我写的,连个快读都没有):
#include<cstdio> int heap[10001],a[10001],size=0; void swap(int &a,int &b) { int t=a; a=b; b=t; } void push(int k) { heap[++size]=k; int r=size; while(r>1&&heap[r]<heap[r>>1]) { swap(heap[r],heap[r>>1]); r>>=1; } } int pop() { int ans=heap[1],r=1,t; heap[1]=heap[size--]; while(r<<1<=size) { t=r<<1; if(heap[t] 4000 >=heap[t+1]) t++; if(heap[r]<heap[t]) return ans; swap(heap[r],heap[t]); r=t; } return ans; } int main() { int n,s=0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); push(a[i]); } while(size>1) { int a=pop(),b=pop(); push(a+b); s+=a+b; } printf("%d",s); }
偷懒的priority_queue做法(加了快读都比上面短):
#include <cstdio> #include <queue> using namespace std; #define gc getchar() priority_queue<int,vector<int>,greater<int> > q; //小根堆 小根堆 小根堆 inline int gnum(){ int a=0;char c=gc;bool f=0; for(;(c<'0'||c>'9')&&c!='-';c=gc); if(c=='0') c=gc,f=1; for(;c>='0'&&c<='9';c=gc) a=(a<<1)+(a<<3)+c-'0'; if(f) return -a; return a; } int main(){ int n=gnum(),ans=0; for(int i=1;i<=n;i++) q.push(gnum()); while(q.size()>1){ int u=q.top(); q.pop(); int v=q.top(); q.pop(); ans+=u+v; q.push(u+v); } printf("%d",ans); }
至于石子合并?我刚开始是拒绝的。。其实是我发现自己不会写了 后来想了一会,好像有辣么一点印象。。然后循环的结果。。我是崩溃的。。里面的循环傻傻分不清了喂。。
于是我又去做了一遍。。
这题的原理:经典的环形dp
首先一看环形dp,先将数组扩到2n..
然后状态转移方程大概就是:
f[i][j]=min{f[i][k]+f[k][j]+sigma(a[i]..a[j])} ( k=[i..j) )
其中的sigma可以通过前缀和。。
然后枚举i,j,k就好了(我习惯枚举长度和i来推j..看个人喜好吧。。)
石子合并代码(这里要求最小值和最大值,应该是f最小值,g最大值)
然后就是我的a数组直接原地求前缀和了。。看上去有点方,但是能看。。
#include <cstdio> #define gc getchar() const int N=204; const int INF=~0U>>2; int f ,g ,a ; inline int gnum(){ int a=0;char c=gc;bool f=0; for(;(c<'0'||c>'9')&&c!='-';c=gc); if(c=='-') c=gc,f=1; for(;c>='0'&&c<='9';c=gc) a=(a<<1)+(a<<3)+c-'0'; if(f) return -a; return a; } int main(){ int n=gnum(); for(int i=1;i<=n;i++) a[i]=a[i-1]+gnum(); for(int i=n+1;i<=n<<1;i++) a[i]=a[i-1]+a[i-n]-a[i-n-1]; for(int l=2;l<=n;l++) for(int i=1;i+l-1<n<<1;i++){ int j=i+l-1; f[i][j]=INF; g[i][j]=-INF; for(int k=i;k<j;k++){ int a1=f[i][k]+f[k+1][j]+a[j]-a[i-1], a2=g[i][k]+g[k+1][j]+a[j]-a[i-1]; if(f[i][j]>a1) f[i][j]=a1; if(g[i][j]<a2) g[i][j]=a2; } } int minn=INF,maxn=-INF; for(int i=1;i<=n;i++){ int j=i+n-1; if(f[i][j]<minn) minn=f[i][j]; if(g[i][j]>maxn) maxn=g[i][j]; } printf("%d\n%d",minn,maxn); }
好的。。扯了这么长时间= =
能看到这里的都是真爱吧(什么鬼)
上面的原理搞清楚了,
然后中间就用循环乱搞即可。。
(话说我第一遍还看错题了什么鬼)
本题的代码(其实是上面的合并,但是我重构啦♪(^∇^*)
#include <cstdio> #include <queue> using namespace std; #define gc getchar() priority_queue<int,vector<int>,greater<int> > q; const int INF=~0U>>1; int a[25],b[25],f[25][25],s[25]; inline int gnum(){ int a=0;char c=gc; bool f=0; for(;(c<'0'||c>'9')&&c!='-';c=gc); if(c=='-') c=gc,f=1; for(;c>='0'&&c<='9';c=gc) a=(a<<1)+(a<<3)+c-'0'; if(f) return -a; return a; } int main(){ int n=gnum(),m=gnum(),k=gnum(),sum=0; for(int i=1;i<=n;i++) q.push(gnum()); while(k--){ int all=0; for(int i=1;i<=m;i++){ int x=gnum(); a[x]=q.top(); q.pop(); all+=a[x]; }q.push(all); //有一件事你们不要和我学。。push进去的不要用dp出来的结果,而是要把质量的和push进去。。 //刚刚又有一个小伙子过来吐槽。。。(记住这么干会全WA)。。 for(int i=1;i<=m;i++) a[m+i]=a[i]; for(int i=1;i<=m<<1;i++) s[i]=s[i-1]+a[i]; for(int l=2;l<=m;l++) for(int i=1;i+l-1<m<<1;i++){ int j=i+l-1; f[i][j]=INF; for(int k=i;k<j;k++){ int ans=f[i][k]+f[k+1][j]+s[j]-s[i-1]; if(f[i][j]>ans) f[i][j]=ans; } } int minn=INF; for(int i=1;i<=m;i++) if(f[i][i+m-1]<minn) minn=f[i][i+m-1]; sum+=minn; } printf("%d",sum); }
嗯 就是这样。。
完成了一次传说中的三合一。。
这三个题难度保证递增,然而会了前两个怎么都能拼出第三个嘛。。
然而我几乎忘了石子合并怎么做( ⊙o⊙ )~~
唉我还是太弱了
相关文章推荐
- 洛谷 p1880 石子合并 区间dp
- 动态规划——洛谷1880石子合并
- 洛谷1090 合并果子
- 【竞赛题目对比】& 合并吧!石子合并 与 合并果子 &
- 洛谷-P1090 合并果子
- 洛谷 1880 石子合并 DP
- 洛谷 P1090 合并果子
- 洛谷1880 石子合并
- 洛谷 1880合并果子
- DP 洛谷 P1880 [NOI1995]石子合并
- 洛谷[1090]合并果子
- 洛谷 P1090 合并果子
- 洛谷P1880 石子合并
- 洛谷P1880 石子合并
- 洛谷1880 石子合并
- 洛谷 P1090 合并果子
- 洛谷 P1090 合并果子
- 洛谷P1090 合并果子
- [洛谷 P1090]合并果子 --- 手打二叉堆
- [动态规划] 洛谷P1063 能量项链 (石子合并)