191102Practice DP部分

zgs说大家都爱考DP
于是我们就练DP
而且这些DP连升级版(树状啊,状压之类的)都不是

T4 Happy

转换一下就是0/1背包
和昨天晚上的T1一样,也可以用回溯法

一下子跳到0/1背包的坑里去了——(i表示状态的总重,j表示到该状态的方案)
0/1背包的状移方程
f i , j = m a x { f i , j 1 , f i v i , j 1 + w i } f_{i,j}=max\{\mathop{f_{i,j-1}}\limits_{不放},\mathop{f_{i-v_i,j-1}+w_i}\limits_{放}\}
完全背包的状移方程
f i , j = m a x { f i , j , f i v i , j 1 + w i } f_{i,j}=max\{\mathop{f_{i,j}}\limits_{不放},\mathop{f_{i-v_i,j-1}+w_i}\limits_{放}\}
减不减1就是区别

关键来了 敲黑板

万一你放不进去呢
于是就有
f i , j = f i , j 1 f_{i,j}=f_{i,j-1}
0/1背包或完全背包都是这样

放上代码

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
#define NNN 30
#define NN 30010
int n,m;
int v[NNN],p,w[NNN];
int f[NN][NNN];

inline int in{
	int i=0,f=1;char ch;
	while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
	if(ch=='-'){
		ch=getchar();f=-1;
	}
	while(ch>='0'&&ch<='9')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

int main(){
	
//	freopen("happy.in","r",stdin);
//	freopen("happy.out","w",stdout);
	
	n=in,m=in;
	for(re int i=1;i<=m;i++)v[i]=in,p=in,w[i]=v[i]*p;
//	for(re int i=1;i<=m;i++)printf("%d\n",w[i]);
	
	for(re int i=1;i<=n;i++)
		for(re int j=1;j<=m;j++){
			if(i-v[j]>=0)
				f[i][j]=max(f[i][j-1]/*不加*/,f[i-v[j]][j-1]+w[j]/*加*/);
			else f[i][j]=f[i][j-1];
		}
	
	printf("%d\n",f[n][m]);
//	for(re int i=1;i<=n;i++)
//		printf("%d ",f[i][m]);
	return 0;
}

然后偷懒粘一个zgs的DFS

#include<bits/stdc++.h> 
using namespace std; 
 
int n,m,s,MAX,i,v[30],w[30]; 
 
void serch(int n,int s,int k)  //当前剩余总金额n,当前乘积总和s,到第k个物品  
{   
    for(int i=0;i<=1;i++)               //第k种物品选与不选  
    { 
        if(i==1 && n-v[k]>0)            //如果k种物品可以选  
        { 
            n=n-v[k]; 
            s=s+v[k]*w[k];  
            if(s>MAX) MAX=s;            //记录此时的最大值  
        }  
        if(k<m) serch(n,s,k+1);  
    } 
} 
 
int main() 
{ 
  freopen("happy.in","r",stdin); 
  freopen("happy.out","w",stdout); 
  scanf("%d %d",&n,&m);             //读入允许的总金额n和物品数量m  
  for (i=1;i<=n;i++)  
    scanf("%d%d",&v[i],&w[i]);      // 读入每个物品的价格v[i]和重要度w[i] 
  s=0;MAX=0; 
  serch(n,s,1); 
  cout<<MAX<<endl; 
}

T5 Ball

我竟然想出了正解!!!好激动哦
自认为我的代码比zgs的好看一些

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
#define NN 50
int n,m;
int f[NN/*传的次数*/][NN/*人的编号*/]; 

inline int in{
	int i=0,f=1;char ch;
	while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
	if(ch=='-'){
		ch=getchar();f=-1;
	}
	while(ch>='0'&&ch<='9')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

int main(){
	
//	freopen("ball.in","r",stdin);
//	freopen("ball.out","w",stdout);
	
	n=in,m=in;
	f[0][0]=1;
	for(re int i=1;i<=m;i++)
		for(re int j=0;j<n;j++){
			int j1=j-1,j2=j+1;
			if(j1==-1)j1=n-1;
			if(j2==n)j2=0;
			f[i][j]=max(f[i-1][j1]+f[i-1][j2],f[i][j]);
		}
	
	printf("%d",f[m][0]);
	return 0;
}

T6 Chorus

spojP129
没那么复杂——不过就是一个最长不上升子序列长度+最长不下降子序列长度-1

复习一下DP求最长上升子序列(上升跟不下降有区别的哦~)
对于每一个数字,与其前的每一个数字比较
找到合法的,且目前构成的最长上升子序列最长的
把这个数拼到它后面

小心最后求的是从整个队列里面去掉的人
所以要用n减去所有选出来的构成“峰”的人
最后-1,注意一下容斥原理——中间那个人被算了2次

sol

#include<bits/stdc++.h>
using namespace std;
#define re register
#define in Read()
#define NNN 110
int n,t[NNN];
int f[NNN],g[NNN];

inline int in{
 int i=0,f=1;char ch;
 while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
 if(ch=='-')f=-1,ch=getchar();
 while(ch>='0'&&ch<='9')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
 return i*f;
}

inline void Get(){
 for(re int i=1;i<=n;i++)
  for(re int j=1;j<i;j++)
   if(t[i]>t[j]&&f[i]<f[j]+1)
    f[i]=f[j]+1;
 for(re int i=n;i>=1;i--)
  for(re int j=n;j>i;j--)
   if(t[i]>t[j]&&g[i]<g[j]+1)
    g[i]=g[j]+1;
}

int main(){
 n=in;
 for(re int i=1;i<=n;i++)t[i]=in;
 
 Get();
 
 int ans=99999;
 for(re int i=1;i<=n;i++)
  ans=min(ans,n-f[i]-g[i]-1);
 
 printf("%d\n",ans);
// for(re int i=1;i<=n;i++)printf("%d ",f[i]);
// printf("\n");
// for(re int i=1;i<=n;i++)printf("%d ",g[i]);
 return 0;
} 

T7 Tortoise

龟棋
我能过样例2不能过1的代码竟然给了我10分

然而记录每一个状态的数组要用5维
f i , x 1 , x 2 , x 3 , x 4 f_{i,x1,x2,x3,x4}
于是空间复杂度 O ( N × 4 0 4 ) O(N\times 40^4) ,要炸
给出两个优化
1
由于 x 4 = ( i x 1 x 2 x 3 ) ÷ 4 x_4=(i-x_1-x_2-x_3)\div4
那么可以省掉 x 4 x_4 一维
于是空间复杂度简化为 O ( N × 4 0 3 ) O(N\times 40_3)

2
利用滚动数组
因为每个i都与i-1,i-2,i-3,i-4有关
然鹅我不会滚动数组

所以使用优化1就是介各亚子的啦

本代码是以前推后的方法

#include<bits/stdc++.h>
using namespace std;
#define re register
#define in Read()
#define NNN 41
#define NN 400
int n,m,tmp;
int f[NN][NNN][NNN][NNN],p[NN],cnt[5];

inline int in{
 int i=0,f=1;char ch;
 while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
 if(ch=='-')f=-1,ch=getchar();
 while(ch>='0'&&ch<='9')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
 return i*f;
}

int main(){
 n=in,m=in;
 for(re int i=1;i<=n;i++)p[i]=in;
 for(re int i=1;i<=m;i++)tmp=in,cnt[tmp]++;
 
 f[1][0][0][0]=p[1];
 for(re int i=1;i<=n;i++){
  
  for(re int x1=0;x1<=cnt[1];x1++)
   for(re int x2=0;x2<=cnt[2];x2++)
    for(re int x3=0;x3<=cnt[3];x3++){
     
     int x4=(i-x1-x2*2-x3*3)/4;
     int k=f[i][x1][x2][x3];
     if(i+1<=n&&x1<cnt[1])f[i+1][x1+1][x2][x3]=max(f[i+1][x1+1][x2][x3],k+p[i+1]);
     if(i+2<=n&&x2<cnt[2])f[i+2][x1][x2+1][x3]=max(f[i+2][x1][x2+1][x3],k+p[i+2]);
     if(i+3<=n&&x3<cnt[3])f[i+3][x1][x2][x3+1]=max(f[i+3][x1][x2][x3+1],k+p[i+3]);
     if(i+4<=n&&x4<cnt[4])f[i+4][x1][x2][x3]=max(f[i+4][x1][x2][x3],k+p[i+4]);
     
    }
 }
 
 printf("%d\n",f[n][cnt[1]][cnt[2]][cnt[3]]);
 return 0;
} 

不要越界不要越界不要越界!!!
每一个xn都要枚完,以便于求出x4
每个状态防止越界
枚举每个点,可以不枚举终点因为前面的点已经可以达到它了

下面给出到后瞻前的代码

相比于前面的方法
同样要枚完每个xn
一样要防越界,因为我们在瞻前
可以不枚举起点

#include<bits/stdc++.h>
using namespace std;
#define re register
#define in Read()
#define NNN 41
#define NN 400
int n,m,tmp;
int f[NN][NNN][NNN][NNN],p[NN],cnt[5];

inline int in{
 int i=0,f=1;char ch;
 while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
 if(ch=='-')f=-1,ch=getchar();
 while(ch>='0'&&ch<='9')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
 return i*f;
}

int main(){
 n=in,m=in;
 for(re int i=1;i<=n;i++)p[i]=in;
 for(re int i=1;i<=m;i++)tmp=in,cnt[tmp]++;
 
 f[1][0][0][0]=p[1];
 for(re int i=1;i<=n;i++){
  
  for(re int x1=0;x1<=cnt[1];x1++)
   for(re int x2=0;x2<=cnt[2];x2++)
    for(re int x3=0;x3<=cnt[3];x3++){
     
     int x4=(i-x1-x2*2-x3*3)/4;
     if(i-1>=0&&x1>0)f[i][x1][x2][x3]=max(f[i][x1][x2][x3],f[i-1][x1-1][x2][x3]+p[i]);
     if(i-2>=0&&x2>0)f[i][x1][x2][x3]=max(f[i][x1][x2][x3],f[i-2][x1][x2-1][x3]+p[i]);
     if(i-3>=0&&x3>0)f[i][x1][x2][x3]=max(f[i][x1][x2][x3],f[i-3][x1][x2][x3-1]+p[i]);
     if(i-4>=0&&x4>0)f[i][x1][x2][x3]=max(f[i][x1][x2][x3],f[i-4][x1][x2][x3]+p[i]);
    }
 }
 
 printf("%d\n",f[n][cnt[1]][cnt[2]][cnt[3]]);
 return 0;
 }
发布了26 篇原创文章 · 获赞 3 · 访问量 911

猜你喜欢

转载自blog.csdn.net/Antimonysbguy/article/details/102872153
DP
DP?