[题解] cogs 2240 架设电话线路
http://cogs.pro:8080/cogs/problem/problem.php?pid=2240
与洛谷P2885几乎一致,https://www.luogu.org/problemnew/show/P2885,双倍经验。
定义dp[i][j]表示前i棵树的最大花费并且第i 棵树高度为j的。
我们可以想到这某一棵树的高度与它前边的树有直接的关系,不难有一种想法。
枚举第几棵树1->n
枚举每棵树的高度$h[i]-> max \{ h[1],h[2]...h[n] \} $
枚举第i棵树前边那棵树也就是第i-1棵树的高度。
现在我们想状态转移方程:
当第i棵数的高度为j时,那么需要花费$(j-h[i])^2$,与前边好要有连起来那就需要找到abs(j-dp[i-1][h[i]->maxh])中的最小值。
所以$dp[i][j]=min(dp[i][j],(j-h[i]^2+(j-dp[i-1][h[i]->maxh]) \times c))$.
那么算法时间复杂度为$O(n \times h \times h)$.
时间复杂度虽然比较高,奈何cogs数据比较水啊,勉强可以过。
#include <cstdio> #include <iostream> #include <cstdlib> #include <cstring> using namespace std; int min(int a,int b){ return a<b?a:b; } int n,a[100006],c,ans,f[100006][101]; inline int read() { char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar(); return x; } int main() { freopen("phonewire.in","r",stdin); freopen("phonewire.out","w",stdout); ans=0x7fffffff; scanf("%d%d",&n,&c); for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<=n;i++) for(int j=0;j<=100;j++) f[i][j]=1234567890; for(int i=0;i<=100;i++)f[1][i]=(i-a[1])*(i-a[1]); for(int i=2;i<=n;i++) { for(int j=a[i];j<=100;j++) { for(int k=a[i-1];k<=100;k++) { f[i][j]=min(f[i][j],f[i-1][k]+(j-a[i])*(j-a[i])+abs(k-j)*c); } } } for(int i=1;i<=100;i++)ans=min(ans,f[n][i]); printf("%d",ans); fclose(stdin);fclose(stdout); }
然而洛谷上不吸氧的话就需要优化一下了(以下话语来自洛谷管理---redbag)
不难发现,每次转移是个开口向上的二次函数(可以自己算算),然后我们枚举上一棵树的高度的过程中,
如果随着高度的增加费用增加了,就可以不用继续转移了。
#include<bits/stdc++.h> using namespace std; inline int read() { char s; int k=0,base=1; while((s=getchar())!='-'&&s!=EOF&&!(s>='0'&&s<='9')); if(s==EOF)exit(0); if(s=='-')base=-1,s=getchar(); while(s>='0'&&s<='9') { k=k*10+(s-'0'); s=getchar(); } return k*base; } inline void write(int x) { if(x<0) { putchar('-'); write(-x); } else { if(x/10)write(x/10); putchar(x%10+'0'); } } int n,c,p,x,s,mh; int h[100100]; int f[100100][100]; int main() { n=read(); c=read(); for (register int i=1; i<=n; i++) { h[i]=read(); if (h[i]>mh) mh=h[i]; } memset(f,1,sizeof(f)); for (register int i=h[1]; i<=100; i++) f[1][i]=(i-h[1])*(i-h[1]); for (register int i=2; i<=n; i++) { for (register int j=h[i]; j<=mh; j++) //hm:电线杆的最大高度 { s=(j-h[i])*(j-h[i]);//先算出来快些? p=233333333; for (register int k=h[i-1]; k<=mh; k++) { x=f[i-1][k]+s+c*abs(k-j); //下面和这一句是等效的,似乎快点?f[i][j]=min(f[i][j],x); if (x<f[i][j]) { f[i][j]=x; } if (x>p) break;//比上一个更多就不用转移了 p=x; } } } int ans=f[n][h[n]]; for (register int i=h[n]+1; i<=100; i++) ans=min(ans,f[n][i]); //找答案 printf("%d",ans); return 0; }
除特别注明外,本站所有文章均为Manjusaka丶梦寒原创,转载请注明来自出处