HDU - 3507
入门题。
#include
#include
#include
#include
using namespace std;
#define ll long long
const int maxn = 5e5 + 10;
ll d[maxn],sum[maxn],dp[maxn];
int n,m;
ll getx(int j,int k)
{ return dp[j] + sum[j]*sum[j] - (dp[k] + sum[k]*sum[k]); }
ll gety(int j,int k)
{ return 2*sum[j]-2*sum[k]; }
ll getval(int i,int j)
{ return dp[j] + (sum[i]-sum[j])*(sum[i]-sum[j]) + m; }
int que[maxn];
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i = 1; i <= n; i++)
{
scanf("%I64d",&d[i]);
sum[i] = d[i];
if(i) sum[i] += sum[i-1];
dp[i] = 0;
}
int head = 0,tail = 1;
que[0] = 0;
for(int i = 1; i <= n; i++)
{
while(tail > head+1 && getx(que[head+1],que[head]) <= sum[i]*gety(que[head+1],que[head])) ++head;
dp[i] = getval(i,que[head]);
while(tail > head+1 && getx(i,que[tail-1])*gety(que[tail-1],que[tail-2]) <= getx(que[tail-1],que[tail-2])*gety(i,que[tail-1])) tail--;
que[tail++] = i;
}
printf("%I64d\n", dp[n]);
}
return 0;
}
HDU - 2829
题目大意:给你m个炸弹炸铁路,最后问你铁路最小价值和是多少。
思路:可以推出一个很神奇的式子:
dp[i][j]=min(dp[k][j−1]+val(k+1,i));
val(k+1,i)=∑im=k+1d[m]∗(sum[m−1]−sum[k])
所以有:
val(k+1,i)=∑im=k+1d[m]∗sum[m−1]−∑im=k+1d[m]∗sum[k]
然后设
fn[i]=∑im=1d[m]∗sum[m−1]
发现这个fn[i,k] = fn[i] - fn[k-1],可以O(1)查询,满足区间减法!所以这个时候我们的前提条件就准备好啦。
代码我是设的 sum[i]=∑i−1m=1d[m] 所以fn的表示有一点不同,sum[i]的表示也不同。
#include
#include
#include
#include
using namespace std;
const int maxn = 1e3+10;
#define ll long long
int d[maxn];
ll dp[maxn][maxn],fn[maxn],sum[maxn];
ll getval(int i,int k,int j)
{
ll temp = fn[i] - fn[k] - sum[k+1]*(sum[i+1]-sum[k+1]);
return temp+dp[k][j-1];
}
ll getx(int j,int k,int lev)
{
ll a = dp[j][lev-1] - fn[j] + sum[j+1]*sum[j+1];
ll b = dp[k][lev-1] - fn[k] + sum[k+1]*sum[k+1];
return a-b;
}
int que[maxn];
ll gety(int j,int k)
{ return sum[j+1] - sum[k+1]; }
int n,m;
int main()
{
//freopen("D:\\in.txt","r",stdin);
while(scanf("%d%d",&n,&m) && n+m)
{
for(int i = 1; i <= n; i++) scanf("%d",&d[i]);
for(int i = 1; i <= n; i++)
{
sum[i] = d[i-1] + sum[i-1];
fn[i] = sum[i]*d[i] + fn[i-1];
}
sum[n+1] = d[n] + sum[n];
for(int i = 1; i <= n; i++)
dp[i][1] = getval(i,0,1);
for(int j = 2; j <= m+1; j++)
{
int h = 0, t = 1;
que[h] = j-1;que[t++] = j;
for(int i = 0; i <= j; i++) dp[i][j] = 0;
for(int i = j+1; i <= n;i++)
{
int T = sum[i+1];
while(h+1 < t && getx(que[h+1],que[h],j) <= T*gety(que[h+1],que[h])) h++;
dp[i][j] = getval(i,que[h],j);
while(h+11],j)*gety(que[t-1],que[t-2]) <= getx(que[t-1],que[t-2],j)*gety(i,que[t-1])) t--;
que[t++] = i;
}
}
printf("%I64d\n",dp[n][m+1]);
}
return 0;
}
HDU - 1300
题意:这题其实最难的是题意,其实我也就看了个大概
有n种珍珠,每种珍珠有一个需求量,有单价,排在前面的可以用后面的价格买,排在后面的不能用前面的价格买。买的时候是这个式子: cost=(num+10)∗unit
思路:先列dp方程
dp[i]=dp[k]+val(k+1,i)
val(k+1,i)=c[i]∗(sum[i]−sum[k]+10)
然后的事情就很简单了。
#include
#include
#include
#include
using namespace std;
const int maxn = 105;
int d[maxn],c[maxn];
int sum[maxn];
int dp[maxn],q[maxn];
int getavl(int i,int k)
{
return dp[k] + c[i]*(sum[i]-sum[k] + 10);
}
int getx(int j,int k){ return dp[j]-dp[k]; }
int gety(int j,int k){ return sum[j]-sum[k]; }
int n;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
scanf("%d%d",&d[i],&c[i]);
sum[i] = d[i] + sum[i-1];
}
int h = 0,t = 0;
q[t++] = 0;
for(int i = 1; i <= n; i++)
{
int T = c[i];
while(h+1q[h+1],q[h]) <= gety(q[h+1],q[h])*T) ++h;
dp[i] = getavl(i,q[h]);
while(h+1q[t-1])*gety(q[t-1],q[t-2]) <= getx(q[t-1],q[t-2])*gety(i,q[t-1])) --t;
q[t++] = i;
}
cout << dp[n] << endl;
}
return 0;
}
HDU - 3480
题意: 把一个序列分成m段,使每段的最大值减最小值的平方和最小。
思路:我们需要先排个序,然后再处理
可以推出dp方程: dp[i][j]=min(dp[k][j−1]+(d[i]−d[k+1])∗(d[i]−d[k+1]));
由于数组很大,所以需要一下滚动数组。
#include
#include
#include
#include
using namespace std;
const int maxn = 1e4 + 10;
int d[maxn];
int dp[maxn][2];
int getval(int i,int k,int j)
{
int last = j==0?1:0;
return dp[k][last] + (d[i]-d[k+1])*(d[i]-d[k+1]);
}
int getx(int j,int k,int th)
{
int last = th==0?1:0;
return dp[j][last]+d[j+1]*d[j+1] - (dp[k][last] + d[k+1]*d[k+1]);
}
int gety(int j,int k){return d[j+1]-d[k+1];}
int q[maxn];
int n,m;
int main()
{
int t,kase=1;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d",&d[i]);
sort(d + 1, d + n + 1);
int th = 0,last = 0;
for(int i = 1; i <= n; i++) dp[i][0] = getval(i,0,th);
for(int j = 2; j <= m; j++)
{
th = last?0:1;
for(int i = 1; i <= j; i++) dp[i][th] = 0;
int h = 0,t = 0;
q[t++] = j-1; q[t++] = j;
for(int i = j+1; i <= n; i++)
{
int T = d[i]*2;
while(h+1q[h+1],q[h],th) <= T*gety(q[h+1],q[h])) h++;
dp[i][th] = getval(i,q[h],th);
while(h+1q[t-1],th)*gety(q[t-1],q[t-2]) <= getx(q[t-1],q[t-2],th)*gety(i,q[t-1])) t--;
q[t++] = i;
}
last = th;
}
printf("Case %d: %d\n", kase++, dp[n][th]);
}
return 0;
}
BZOJ - 1010
题意:中文题
思路: dp[i]=dp[j]+i−(j+1)+sum[i]−sum[j];
#include
#include
#include
#include
using namespace std;
#define ll long long
const int maxn = 5e4 + 10;
ll d[maxn],sum[maxn],dp[maxn];
int que[maxn];
int n,m;
ll getval(int i,int j)
{
ll temp = i - (j+1) + sum[i] - sum[j];
return (ll)dp[j] + (temp-m)*(temp-m);
}
ll getx(int j,int k)
{
ll a = dp[j] + (j+1+sum[j]+m)*(j+1+sum[j]+m);
ll b = dp[k] + (k+1+sum[k]+m)*(k+1+sum[k]+m);
return a-b;
}
ll gety(int j,int k)
{
return j+sum[j]-k-sum[k];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
{
scanf("%lld",&d[i]);
sum[i] = d[i];
sum[i] += sum[i-1];
dp[i] = 0;
}
int head = 0, tail = 1;
que[0] = 0;
for(int i = 1; i <= n; i++)
{
ll T = 2*(i+sum[i]);
while(tail > head+1 && getx(que[head+1],que[head]) <= T*gety(que[head+1],que[head])) head++;
dp[i] = getval(i,que[head]);
while(tail > head+1 && getx(i,que[tail-1])*gety(que[tail-1],que[tail-2]) <= getx(que[tail-1],que[tail-2])*gety(i,que[tail-1])) tail--;
que[tail++] = i;
}
printf("%lld\n",dp[n]);
return 0;
}
BZOJ - 1096
中文题+2
思路:
dp[i]=dp[j]+dp[k]−fn[i]+fn[k]+d[i][0]∗(sum[i]−sum[k])+d[i][2]
#include
#include
#include
#include
using namespace std;
#define ll long long
const int maxn = 1e6 + 10;
ll d[maxn][3];
ll sum[maxn], fn[maxn];
int que[maxn];
ll dp[maxn];
ll setval(int i,int k)
{
ll temp = dp[k] - fn[i] + fn[k] + d[i][0]*(sum[i]-sum[k]) + d[i][2];
return temp;
}
ll fx(int j,int k)
{
ll a = dp[j] + fn[j] ;
ll b = dp[k] + fn[k] ;
return a-b;
}
ll fy(int j,int k)
{
return sum[j] - sum[k];
}
int main()
{
//freopen("D:\\in.txt","r",stdin);
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
scanf("%lld%lld%lld",&d[i][0],&d[i][1],&d[i][2]);
ll temp = d[i][0]*d[i][1];
fn[i] = temp; sum[i] = d[i][1];
fn[i] += fn[i-1]; sum[i] += sum[i-1];
}
int h = 0,t = 1;
que[0] = 0;
for(int i = 1; i <= n; i++)
{
ll T = d[i][0];
while(h+1
dp[i] = setval(i,que[h]);
while(h+1
que[t++] = i;
}
printf("%lld\n",dp[n]);
return 0;
}