luogu2885
P2885 [USACO07NOV]电话线Telephone Wire
给出若干棵树的高度,你可以进行一种操作:把某棵树增高h,花费为h*h。
操作完成后连线,两棵树间花费为高度差*定值c。
求两种花费加和最小值。
输入输出样例
输入 #1
5 2
2
3
5
1
4
输出 #1
View Code
ac
15
sol:显然暴力的dp很容易得到,dp[i][j]表示第i个高度为j个最小代价
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll S=0; char ch=' '; bool f=0; while(!isdigit(ch)) {f|=(ch=='-'); ch=getchar();} while(isdigit(ch)) {S=(S<<3)+(S<<1)+(ch-'0'); ch=getchar();} return (f)?(-S):(S); } #define R(x) x=read() inline void write(ll x) { if(x<0) {putchar('-'); x=-x;} if(x<10) {putchar(x+'0'); return;} write(x/10); putchar(x%10+'0'); } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=100005,M=105,inf=0x3f3f3f3f; int n,c,hig[N],mx,ans; int dp[N][M]; inline int sqr(int x){return x*x;} int main() { freopen("luogu2885_data.in","r",stdin); int i,j,k; R(n); R(c); for(i=1;i<=n;i++) { R(hig[i]); mx=max(mx,hig[i]); } memset(dp,63,sizeof dp); dp[1][hig[1]]=0; for(i=1;i<=mx-hig[1];i++) dp[1][hig[1]+i]=i*i; for(i=2;i<=n;i++) { for(j=hig[i];j<=mx;j++) for(k=hig[i-1];k<=mx;k++) { dp[i][j]=min(dp[i][j],dp[i-1][k]+sqr(j-hig[i])+c*abs(j-k)); } } ans=inf; for(i=hig[n];i<=mx;i++) ans=min(ans,dp[n][i]); Wl(ans); return 0; }
然后发现容易拆开abs分类讨论一下即可
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) {f|=(ch=='-'); ch=getchar();} while(isdigit(ch)) {s=(s<<3)+(s<<1)+(ch^48); ch=getchar();} return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) {putchar('-'); x=-x;} if(x<10) {putchar(x+'0'); return;} write(x/10); putchar((x%10)+'0'); } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=100005,inf=0x3f3f3f3f; int n,c,mx,hig[N]; int dp[N][105],mn[105]; inline int sqr(int x){return x*x;} int main() { freopen("luogu2885_data.in","r",stdin); int i,j; R(n); R(c); for(i=1;i<=n;i++) mx=max(mx,hig[i]=read()); memset(dp,63,sizeof dp); for(i=hig[1];i<=mx;i++) dp[1][i]=sqr(i-hig[1]); for(i=2;i<=n;i++) { for(j=0;j<=hig[i-1]-1;j++) mn[j]=inf; for(j=hig[i-1];j<=mx;j++) mn[j]=min(mn[j-1],dp[i-1][j]-c*j); for(j=hig[i];j<=mx;j++) dp[i][j]=min(dp[i][j],mn[j]+sqr(j-hig[i])+c*j); mn[mx+1]=inf; for(j=mx;j>=hig[i-1];j--) mn[j]=min(mn[j+1],dp[i-1][j]+c*j); for(j=hig[i-1]-1;j>=1;j--) mn[j]=mn[hig[i-1]]; for(j=hig[i];j<=mx;j++) dp[i][j]=min(dp[i][j],mn[j]+sqr(j-hig[i])-c*j); // for(j=hig[i];j<=mx;j++) cout<<i<<' '<<j<<' '<<dp[i][j]<<endl; putchar('\n'); } int ans=inf; for(j=hig[n];j<=mx;j++) ans=min(ans,dp[n][j]); Wl(ans); return 0; }
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!