洛谷 P2530 [SHOI2001]化工厂装箱员

题目链接

本来感觉是贪心,但是懒得去想,于是直接\(DP\)出解

\(dp[p][i][j][k]\) 表示到第\(p\)位,有\(i\)\(A\),\(j\)\(B\),\(k\)\(C\)的最小装箱次数

初始化: \(dp[0][0][0][0]=0\) 其余为\(inf\)

转移:

  1. \(i+j+k=10\),\(dp[p][i][j][k]\)更新了\(dp[p][0][j][k]\),\(dp[p][i][0][k]\),\(dp[p][i][j][0]\)
  2. \(i+j+k<10\),继续从队列里取数,\(dp[p][i][j][k]\)更新\(dp[p+ii][i+x][j+y][k+z]\) (ii表示新取几个,\(x\),\(y\),\(z\)分别表示新取的里面的\(A\),\(B\),\(C\)的数量) , 直到\(i+x+j+y+k+z=10\)为止

所有的更新均为取\(min\)

注意转移2可能会数组越界

[Code]

#include <bits/stdc++.h>
using namespace std;

int read(){
	int x=0,flag=1; char c;
	for(c=getchar();!isdigit(c);c=getchar()) if(c=='-') flag=-1;
	for(;isdigit(c);c=getchar()) x=((x+(x<<2))<<1)+(c^48);
	return x*flag;
}

int n;
int a[150];//数组越界注意
int dp[150][15][15][15];

int main() {
    n=read();
    
	for(int i=1;i<=n;i++){
	    char c;
	    scanf(" %c",&c);
	    if(c=='A') a[i]=1;
	    if(c=='B') a[i]=2;
	    if(c=='C') a[i]=3;
	}
	
	memset(dp,0x3f3f3f3f,sizeof(dp));
	dp[0][0][0][0]=0; 
	
	for(int p=0;p<=n;p++)
	for(int i=10;i>=0;i--)
	for(int j=10;j>=0;j--)
	for(int k=10;k>=0;k--)
	if(dp[p][i][j][k]<0x3f3f3f3f){
        if(i+j+k<10){
		    int t=10-i-j-k;
		    int x=0,y=0,z=0;
		    
		    for(int ii=1;ii<=t;ii++){
			    if(a[p+ii]==1) x++;
			    if(a[p+ii]==2) y++;
			    if(a[p+ii]==3) z++;
			    
			    dp[p+ii][i+x][j+y][k+z]=min(dp[p+ii][i+x][j+y][k+z],dp[p][i][j][k]);
			}
		    continue;
		}
		dp[p][0][j][k]=min(dp[p][0][j][k],dp[p][i][j][k]+1);
		dp[p][i][0][k]=min(dp[p][i][0][k],dp[p][i][j][k]+1);
		dp[p][i][j][0]=min(dp[p][i][j][0],dp[p][i][j][k]+1);
	}
	
	int ans=0x3f3f3f3f;
	
	for(int i=0;i<=10;i++)
	for(int j=0;j<=10;j++)
	for(int k=0;k<=10;k++){
	    ans=min(ans,dp[n][i][j][k]+(int)(i>0)+(int)(j>0)+(int)(k>0));
	}
	
	printf("%d\n",ans);
	return 0;
}


posted @ 2020-01-20 10:33  zhuzihan  阅读(95)  评论(0编辑  收藏  举报