HDU 4381 Grid
背包变形。
将操作分为了两类,可以分开处理。
可以dp处理出L[i]:L[i]=-1代表从左到右 i 长度不能被拼凑出来,L[i]!=-1表示从左到右 i 长度能被拼凑出,并且最小费用为L[i]。
反着来一次dp就可以处理出R[i]:R[i]=-1代表从右到左 n+1-i 长度不能被拼凑出来,R[i]!=-1表示从右到左 n+1-i 长度能被拼凑出,并且最小费用为R[i]。
由L[1---i] 与R[i+1---n]可以得出,以i为分割的最优解,于是,枚举 i 即可。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<iostream> using namespace std; typedef long long LL; const double pi=acos(-1.0),eps=1e-8; void File() { freopen("D:\\in.txt","r",stdin); freopen("D:\\out.txt","w",stdout); } inline int read() { char c = getchar(); while(!isdigit(c)) c = getchar(); int x = 0; while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); } return x; } const int maxn=1000+10; int T,n,m; struct X { int op,a,x; }s[maxn]; bool cmp1(X a,const X b) { return a.a<b.a; } bool cmp2(X a,const X b) { return a.a>b.a; } int L[maxn],R[maxn]; int main() { scanf("%d",&T); int cas=1; while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&s[i].op,&s[i].a,&s[i].x); sort(s+1,s+1+m,cmp1); memset(L,-1,sizeof L); L[0]=0; for(int i=1;i<=m;i++) { if(s[i].op!=1) continue; for(int j=n;j>=0;j--) { if(L[j]==-1) continue; if(j+s[i].x>s[i].a) continue; if(L[j+s[i].x]==-1) L[j+s[i].x]=L[j]+1; else L[j+s[i].x]=min(L[j+s[i].x],L[j]+1); } } memset(R,-1,sizeof R); R[n+1]=0; sort(s+1,s+1+m,cmp2); for(int i=1;i<=m;i++) { if(s[i].op!=2) continue; for(int j=1;j<=n+1;j++) { if(R[j]==-1) continue; if(j-s[i].x<s[i].a) continue; if(R[j-s[i].x]==-1) R[j-s[i].x]=R[j]+1; else R[j-s[i].x]=min(R[j-s[i].x],R[j]+1); } } int ans1=0,ans2=0; for(int i=0;i<=n;i++) { int a1=0,a2=0,b1=0,b2=0; for(int j=i;j>=0;j--) { if(L[j]==-1) continue; a1=j,a2=L[j]; break; } for(int j=i+1;j<=n+1;j++) { if(R[j]==-1) continue; b1=n+1-j,b2=R[j]; break; } if(a1+b1>ans1) ans1=a1+b1, ans2=a2+b2; else if(a1+b1==ans1) ans2=min(ans2,a2+b2); } printf("Case %d: %d %d\n",cas++,ans1,ans2); } return 0; }