T1:导弹拦截
S1:Q1为最长不上升子序列,Q2为求最长上升子序列.蒟蒻采用的是单调栈的做法,拿求最长上升子序列来说,将a[ 1 ]先入栈,再维护单调上升序列,若大于q[ lenth ],则入栈,若不大于则二分查找,找到单调栈中第一个比它大的数代替,虽然没找到严谨的证明,可以感性理解不断代替大的数的过程,使单调栈的最大值往小的方面发展,是更多数入站,lenth即为答案.Q2用到了Dilworth定理 :最少链划分 = 最长反链长度.
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<cmath> using namespace std; #define e exit(0) #define R register int n,len,h[100010],q[100010]; int main() { freopen("missile.in","r",stdin); freopen("missile.out","w",stdout); scanf("%d",&n); for(R int i=1;i<=n;++i) scanf("%d",&h[i]); q[++len]=h[n]; for(R int i=n-1;i>=1;--i){ if(h[i]>=q[len]) q[++len]=h[i]; else{ int id=lower_bound(q+1,q+1+len,h[i])-q; q[id]=h[i]; } } printf("%d\n",len); memset(q,0,sizeof(q)); len=0; q[++len]=h[1]; for(R int i=2;i<=n;++i){ if(h[i]>=q[len]) q[++len]=h[i]; else{ int id=lower_bound(q+1,q+1+len,h[i])-q; q[id]=h[i]; } } printf("%d",len); return 0; }
T2:整数划分
S2:我们可以看出这是一个区间DP.我们设f[ i ][ j ]表示前i个数有j个乘号时的最大乘积j∈[ 0 , i-1 ].分割点k的位置枚举可以从j开始(第j个乘号即在第j个数的后面).f[ i ][ j ] = max{f[ k ][ j - 1]+getv( k +1 , i )}.getv函数即求区间这段数的大小.但在试题中我们还有求出怎么划分,记录分割点,用前驱数组到回去即可.
#include<iostream> #include<cstring> #include<cstdio> using namespace std; #define e exit(0) #define R register #define ll long long long long T,n,m,ans,lenth,deep,num[110],f[110][110],q[110],lastr[110][110][3]; void cf(long long x) { deep=0; long long len=0,mix[110]; while(x) { mix[++len]=x%10; x/=10; } for(R int i=len;i>=1;--i) num[++deep]=mix[i]; } long long getv(ll x,ll y) { long long sum=0; for(R ll i=x;i<=y;++i) sum=sum*10+num[i]; return sum; } int main() { freopen("separate.in","r",stdin); freopen("separate.out","w",stdout); scanf("%lld",&T); while(T--){ lenth=0; memset(f,0,sizeof(f)); memset(q,0,sizeof(q)); memset(num,0,sizeof(num)); memset(lastr,-1,sizeof(lastr)); scanf("%lld%lld",&n,&m); cf(n); for(R int i=1;i<=deep;++i) f[i][0]=getv(1,i); for(R int i=1;i<=deep;++i) for(R int j=1;j<=deep-1;++j) for(R int k=j;k<=i-1;++k) f[i][j]=max(f[i][j],f[k][j-1]*getv(k+1,i)); for(R int i=1;i<=deep;++i) for(R int j=1;j<=deep-1;++j) for(R int k=j;k<=i-1;++k) if(f[i][j]==f[k][j-1]*getv(k+1,i)){ lastr[i][j][0]=k; lastr[i][j][1]=j-1; lastr[i][j][2]=f[k][j-1]; } printf("%lld\n",f[deep][m-1]); if(f[deep][m-1]==0){ for(R int i=1;i<=deep;++i) printf("%lld ",num[i]); printf("\n"); continue; } long long lax=deep,lay=m-1; while(lax!=-1&&lay!=-1){ if(lastr[lax][lay][2]!=-1) q[++lenth]=lastr[lax][lay][2]; ll nowx=lastr[lax][lay][0],nowy=lastr[lax][lay][1]; lax=nowx,lay=nowy; } long long num=f[deep][m-1]/q[1]; for(R int i=1;i<=lenth-1;++i) q[i]=q[i]/q[i+1]; for(R int i=lenth;i>=1;--i) printf("%lld ",q[i]); printf("%lld\n",num); } return 0; }
T3:Peter 最近在 R 市开了一家快餐店,为了招揽顾客,该快餐店准备推出一种套餐,该套餐由 A 个汉堡,B 个薯条和 C 个饮料组成.为了提高产量,Peter 从著名的麦当劳公司引进了 N 条生产线.所有的生产线都可以生产汉堡,薯条和饮料,由于每条生产线每天所能提供的生产时间是有限的、不同的,而汉堡,薯条和饮料的单位生产时间又不同.这使Peter 很为难,不知道如何安排生产才能使一天中生产的套餐产量最大.请你编一程序,计算一天中套餐的最大生产量.为简单起见,假设汉堡、薯条和饮料的日产量不超过100 个.
S3:没什么思路,只会f[i][A][B][C]的四维DP,显然时间空间都炸穿.看了题解,发现套餐的个数就是A,B,C中最小能组成的套餐数.我们设f[ k ][ i ][ j ]表示前k个选i个A,j个B时最多还能选多少个C.这里有个枚举的优化,我们确定套数的上限maxn=min ( 100/a,min( 100/b , 100/c ) ),那么要枚举的个数上限即为maxn*a 或 maxn * b.咕咕.
原文:https://www.cnblogs.com/xqysckt/p/11391567.html