4.28 DP练习赛

T1:数塔问题

问题描述:相信大家都写过数字三角形问题,题目很简单求最大化一个三角形数塔从上往下走的路径和。走的规则是:(i,j)号点只能走向(i+1,j)或者(i+1,j+1)。如下图是一个数塔,映射到该数塔上行走的规则为:从左上角的点开始,向下走或向右下走直到最底层结束。

   1
   3 8
   2 5 0
   1 4 3 8
   1 4 2 5 0

路径最大和是1+8+5+4+4 = 22,1+8+5+3+5 = 22或者1+8+0+8+5 = 22。

小S觉得这个问题so easy。于是他提高了点难度,他每次ban掉一个点(即规定哪个点不能经过),然后询问你不走该点的最大路径和。
当然他上一个询问被ban掉的点过一个询问会恢复(即每次他在原图的基础上ban掉一个点,而不是永久化的修改)。
输入
第一行包括两个正整数,N,M,分别表示数塔的高和询问次数。
以下N行,第i行包括用空格隔开的i - 1个数,描述一个高为N的数塔。
而后M行,每行包括两个数X,Y,表示第X行第Y列的数塔上的点被小S ban掉,无法通行。
(由于读入数据较大,c或c++请使用较为快速的读入方式)
输出
M行每行包括一个非负整数,表示在原图的基础上ban掉一个点后的最大路径和,如果被ban掉后不存在任意一条路径,则输出-1。
输入输出样例1
5 3
1
3 8
2 5 0
1 4 3 8
1 4 2 5 0
2 2
5 4
1 1
样例输出
17
22
-1
【数据范围】
在这里插入图片描述
题解:常规的数字三角形有两种递推形式,从上到下,从下到上。分别处理出来。可以用这两个数组来算出最大值,上下拼合在一起。再处理出来每行的 最大值和次大值, 对于每一次修改,若这个位置是推出最大值的位置,那么输出次大值,否则表明不影响,输出最大值。
code:

#include<bits/stdc++.h>
#define zero(x)  memset(x,-10,sizeof(x))
#define MAXN 1500
#define ll long long 
 using namespace std;
int n,m;
ll f1[MAXN][MAXN],f2[MAXN][MAXN],a[MAXN][MAXN];
ll max1[MAXN],max2[MAXN];

int main()
{
  freopen("tower.in","r",stdin);
  freopen("tower.out","w",stdout);
  cin>>n>>m;
  for(int i = 1 ;i <= n; i++ )
   for(int j = 1 ;j <= i; j++ )
    scanf("%lld",&a[i][j]);
  
  f1[1][1] = a[1][1];
  for(int i = 2 ; i <= n ; i++)
     for(int j = 1 ; j <= i ; j++)
      f1[i][j] =max(f1[i-1][j],f1[i-1][j-1]) + a[i][j];
  
  for(int i = 1 ;i <= n ; i ++) f2[n][i] = a[n][i];
  for(int i = n-1 ;i >= 1 ; i --)
     for(int j = 1 ; j <= i; j ++)
      f2[i][j]=max(f2[i+1][j],f2[i+1][j+1]) + a[i][j];    
  
  
  for(int i = 1 ;i <= n ;i++ )
   {
     max1[i] = max2[i] = -1e10;
   	 for(int j = 1 ; j <= i; j++)
     {
       ll val = f1[i][j] + max(f2[i+1][j],f2[i+1][j+1]);
	   if(max1[i] == val ) max2[i] = val;
	    else if(max1[i] < val )  max2[i] = max1[i],max1[i] = val;
		 else if(max2[i] < val ) max2[i] = val;   	
	 }   
   }
  
  while(m--)
  {
   int x,y;
   scanf("%d%d",&x,&y);
   if( x==1 && y==1) {printf("-1\n");continue;}
   if(max1[x] == f1[x][y]+max(f2[x+1][y],f2[x+1][y+1])) printf("%lld\n",max2[x]);
    else printf("%lld\n",max1[x]);
  }
  return 0;	
} 
T2: noise

问题描述
FJ有M个牛棚,编号1至M,刚开始所有牛棚都是空的。
FJ有N头牛,编号1至N,这N头牛按照编号从小到大依次排队走进牛棚,每一天只有一头奶牛走进牛棚。第i头奶牛选择走进第p[i]个牛棚。由于奶牛是群体动物,所以每当一头奶牛x进入牛棚y之后,牛棚y里的所有奶牛们都会喊一声“欢迎欢迎,热烈欢迎”,由于声音很大,所以产生噪音,产生噪音的大小等于该牛棚里所有奶牛(包括刚进去的奶牛x在内)的数量。
FJ很讨厌噪音,所以FJ决定最多可以使用K次“清空”操作,每次“清空”操作就是选择一个牛棚,把该牛棚里所有奶牛都清理出去,那些奶牛永远消失。“清空”操作只能在噪音产生后执行。
现在的问题是:FJ应该选择如何执行“清空”操作,才能使得所有奶牛进入牛棚后所产生的噪音总和最小?
输入
第一行,N、M、K。
接下来有N行,每行一个整数,第i行是p[i]。
输出
一个整数,最小的噪音总和。
输入输出样例1
.in
5 1 2
1
1
1
1
1
.out
7
样例解释1:
第1头奶牛进入牛棚且产生噪音后,“清空”牛棚。第3头奶牛进入牛棚且产生噪音后,再次“清空”牛棚。5头奶牛产生的噪音依次是:1,1,2,1,2。如果没有“清空”操作,5头奶牛产生的噪音依次是:1,2,3,4,5。
输入输出样例2
.in
11 2 3
1
2
1
2
1
2
1
2
1
2
1
.out
18
样例解释2:
第3头奶牛进入牛棚1且产生噪音后,“清空”牛棚1。第7头奶牛进入牛棚1且产生噪音后,“清空”牛棚1。
第6头奶牛进入牛棚2且产生噪音后,“清空”牛棚2。
数据范围
对于40%的数据,M=1。
对于60%的数据,1<=N <= 1000。
对于80%的数据,1<=N <= 50000。
对于100%数据, 1<=N <= 1000000, 1<=M<=100, 1<=K<= 500。
题解: 转移方程:f [ i ] [ j ] = min(f [ i ][ j ], f[ i-1 ][ j-k ]+cost(a[ i ] , k )) 其中cost( ) 表示 第 i 个牛棚 的牛被划分为 k 端(使用k次清空机会的噪音大小) cost 的计算 ? 能被完整划分的是1……n/(k+1) 这样的有 (k+1)-n%(k+1) 段 另一部分为1……n/(k+1)+1 这样的有 n%(k+1) 段。就是等差数列求和了。
code

#include<bits/stdc++.h>
#define ll long long 
 using namespace std;
const int MAXN = 1000; 
int n,m,k;

int fance[MAXN];
ll f[1000][1000];
                      
ll costs(int x ,int k1) 
{if(k1 >= x) return x;
return (ll)(x/(k1+1))*(x/(k1+1)+1)/2*((k1+1)-x%(k1+1))+(ll)(x/(k1+1)+1)*(x/(k1+1)+2)/2*(x%(k1+1));
}

int main()
{
  freopen("noise.in","r",stdin);
  freopen("noise.out","w",stdout);
  cin>>n>>m>>k;
  
   for(int i = 1 ;i <= n; i ++)	
   {
    int x; scanf("%d",&x);
    fance[x]++;
   }
   for(int i = 1 ;i <= m ;i++)
    for(int j = 0 ;j <= k ;j++)
     {
      f[i][j] = 1e11;
      for(int k1 = 0 ; k1 <= j ; k1 ++)
	   f[i][j] = min(f[i][j],f[i-1][k1]+costs(fance[i],j-k1));	    
	 }
	 
  cout<<f[m][k];
  return 0;
}
T3:market

问题描述
在这里插入图片描述
输入
在这里插入图片描述
输出
输出m行,每行一个整数,对于每个计划输出最大可能的价值和
输入样例
5 2
5 5 4
1 3 1
3 4 3
6 2 2
4 3 2
3 8
5 9
输出样例
10
12
解释
在这里插入图片描述
数据范围
在这里插入图片描述
题解
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
部分分做法很多。观察数据范围改变设置状态的两维,f[ i ] [ j ]表示前 i 个 物品 获得 j 的价值所需要的最小代价。首先二分确定时间的限制,然后二分不超过M 获得的最大价值。 时间是排序过的,而f 数组是单调的 因为获得更多的价值是一定要增加体积的。

code

#include<bits/stdc++.h>
#define ll long long 
 using namespace std;
const int MAXN= 1000;
struct node{
  int v,w,t;
}a[MAXN];
ll f[1000][100000];
int t[MAXN];
int n,m,maxv = 0,ans;
inline bool myc(node x,node y) { return x.t < y.t; }
int main()
{
  freopen("market.in","r",stdin);
  freopen("market.out","w",stdout);
   
  cin>>n>>m;
  for(int i = 1 ;i <= n; i ++)
   scanf("%d%d%d",&a[i].w,&a[i].v,&a[i].t),t[i]=a[i].t,maxv+=a[i].v; 
  sort(t+1,t+n+1);   sort(a+1,a+n+1,myc);  
  
  
  for(int i = 1 ; i <= maxv; i++) f[0][i] = 1e12; 
  for(int i = 1 ; i <= n ;i++)
   for(int j = 0 ; j <= maxv; j++)
    if( j >= a[i].v ) 
      f[i][j] = min(f[i-1][j],f[i-1][j-a[i].v]+a[i].w);
     else f[i][j] = f[i-1][j];
  for(int i = 1 ; i <= n ;i++)
   for(int j = maxv-1 ; j >= 0; j--)
    f[i][j] = min(f[i][j],f[i][j+1]);
	
   while(m--)
   {
   	 int T,M,J = 0;
   	 scanf("%d%d",&T,&M);
   	 int pos = upper_bound(t+1,t+n+1,T)-t-1;
   	 ans = upper_bound(f[pos],f[pos]+maxv+1,(ll)M)-f[pos]-1;
   	 printf("%d\n",ans);
   }
		
 return 0;
}
T4:value

问题描述在这里插入图片描述
输入
在这里插入图片描述
输出
在这里插入图片描述
输入样例
5
8 2
10 7
5 1
11 8
13 3
输出样例
27
数据范围
在这里插入图片描述

题解:首先的一个贪心策略是,将代价从小到大排序,代价小的先取。总价值可以理解成 选物品本来的价值减去减少的代价。

code

#include<bits/stdc++.h>
 using namespace std;
const int MAXN = 5050;
int ans=-0xffff;
struct node{
  int v,w;
}e[MAXN];

int f[MAXN][MAXN];
int n;

bool myc(node x,node y) { return x.w<y.w; }
int main()
{
  freopen("value.in","r",stdin);
  freopen("value.out","w",stdout);	
  cin>>n;	
  for(int i = 1 ; i <= n ; i ++)
   scanf("%d%d",&e[i].v,&e[i].w);
  
  sort(e+1,e+n+1,myc);
  
  //dfs(1,0,0);
  for(int i = 1 ;i <= n; i ++)
   for(int j = n ;j >= 0 ; j --)
    f[i][j] = max(f[i-1][j],f[i-1][j+1]+e[i].v-e[i].w*j);
  cout << f[n][0] ;
  return 0;
}
T5:gather

问题描述
Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。
当然,她会选择最方便的地点来举办这次集会。
每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意一个农场都能够到达另外一个农场。道路i连接农场A_i和B_i(1 <= A_i <=N; 1 <= B_i <= N),长度为L_i(1 <= L_i <= 1,000)。集会可以在N个
农场中的任意一个举行。另外,每个牛棚中居住者C_i(0 <= C_i <= 1,000)只奶牛。在选择集会的地点的时候,Bessie希望最大化方便的程度(也就是最小化不方便程度)。比如选择第X个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和,(比如,农场i到达农场X的距离是20,那么总路程就是C_i*20)。帮助Bessie找出最方便的地点来举行大集会。
考虑一个由五个农场组成的国家,分别由长度各异的道路连接起来。在所有农场中,3号
和4号没有奶牛居住。

  1     3     4     5
  @--1--@--3--@--3--@[2]
 [1]    |
        2
        |
        @[1]
        2

Bessie可以在五个农场中的任意一个举办集会,下面就是在每个位置举办集会的不方便值。如果Bessie在农场1举办集会,那么每个农场各自的不方便值分别是

  农场 1     0 -- 到达不需要时间!
  农场 2     3 -- 总的距离是 2+1=3  x 1 奶牛 = 3
  农场 3     0 -- 没奶牛!
  农场 4     0 -- 没奶牛!
  农场 5    14 -- 总的距离是 3+3+1=7 x 2 奶牛 = 14

因此,总的不方便值是17。

最小的不方便值是15,当在3号,4号或者5号农场举办集会的时候。
【输入】
第一行:一个整数N
第二到N+1行:第i+1行有一个整数C_i

第N+2行到2*N行,第i+N+1行为3个整数:A_i,B_i和L_i。
输出
第一行:一个值,表示最小的不方便值。
输入输出样例1
.in
5
1
1
0
0
2
1 3 1
2 3 2
3 4 3
4 5 3
.out
15
题解: 两遍dfs 求出以某一个点为集会点的不方便值和。
code

#include<bits/stdc++.h>
#define ll long long 
 using namespace std;
const int MAXN = 100010; 
int n,m;
struct node{
  int y,v,next;
}e[MAXN*3];
int lin[MAXN],len = 0;
ll size[MAXN],f[MAXN],ans = 1e15;
inline void ins(int xx,int yy,int vv){e[++len].next = lin[xx]; lin[xx] = len; e[len].v = vv; e[len].y = yy;}

void dfs1(int x,int fa)
{
  for(int i = lin[x]; i ; i = e[i].next )
  {
   int y = e[i].y;
   if( y == fa) continue;	
   dfs1(y,x);
   size[x] += size[y];   	
   f[1] +=  (ll)e[i].v*size[y];
  }
}

void dfs2(int x,int fa)
{
  for(int i = lin[x]; i ; i = e[i].next )
   {
     int y = e[i].y;	
   	 if( y == fa )  continue;  
   	 f[y] = (ll)(size[1]-size[y])*e[i].v+(ll)(f[x]-size[y]*e[i].v);
	 ans =  min(ans,f[y]);
	 dfs2(y,x);     
   } 
}

int main()
{
  freopen("gather.in","r",stdin);
  freopen("gather.out","w",stdout);
  
  cin>>n;
  for(int i = 1 ;i <= n; i++)  scanf("%d",&size[i]);
  for(int i = 1 ;i < n ; i++)
  {
  	int xx,yy,vv;
  	scanf("%d%d%d",&xx,&yy,&vv);
  	ins(xx,yy,vv);
  	ins(yy,xx,vv);
  }
  dfs1(1,0);
  dfs2(1,0);
  cout<<ans;
} 
T6 array

问题描述:
在炽热的核熔炉中,居住着一位少女,名为灵乌路空。
据说,从来没有人敢踏入过那个熔炉,因为人们畏缩于空所持有的力量——核能。核焰,可融真金。
每次核融的时候,空都会选取一些原子,排成一列。然后,她会将原子序列分成一些段,并将每段进行一次核融。
一个原子有两个属性:质子数和中子数。
每一段需要满足以下条件:
1、同种元素会发生相互排斥,因此,同一段中不能存在两个质子数相同的原子。
2、核融时,空需要对一段原子加以防护,防护罩的数值等于这段中最大的中子数。换句话说,如果这段原子的中子数最大为x,那么空需要付出x的代价建立防护罩。求核融整个原子序列的最小代价和。
输入
第一行一个正整数N,表示原子的个数。
接下来N行,每行两个正整数pi和ni,表示第i个原子的质子数和中子数。
输出
输出一行一个整数,表示最小代价和。
输入输出样例1
.in
5
3 11
2 13
1 12
2 9
3 13
.out
26
数据范围
对于20%的数据,1<=n<=100
对于40%的数据,1<=n<=1000
对于100%的数据,1<=n<=10^5,1<=pi<=n,1<=ni<=2*1e5

题解:利用性质一处理出每个 i 最左端的端点。
得到dp 式子 :
在这里插入图片描述
s[ i ][ j ] 表示从 i 到 j 的最大值,即代价。
code

#include<bits/stdc++.h>
#define FILE "array"
 using namespace std;
const int MAXN = 100005;
int f[MAXN],p[MAXN],n[MAXN],l[MAXN],last[MAXN];
int N;
int main()
{
  freopen(FILE".in","r",stdin);
  freopen(FILE".out","w",stdout);
  cin>>N;
  for(int i = 1 ;i <= N ;i ++) 
  {
   scanf("%d %d",&p[i],&n[i]);
   l[i] = l[i-1];
   if(last[p[i]]) l[i] = max(l[i],last[p[i]]+1);
   last[p[i]] = i;	
  }
  memset(f,10,sizeof(f));
  f[0] = 0 ;
  for(int i = 1 ;i <= N ;i ++)
   {
   	 f[i] = f[i-1] + n[i];
   	 int maxx = 0 ;
   	 for(int j = i ; j >= l[i] ;j --)
   	 {
	  maxx = max(maxx,n[j]);
	  f[i] = min(f[i],f[j-1] + maxx);	
	 }
   }
  cout << f[N] ; 
  return 0;	
} 

然而这个做法只有60分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值