2018.8.17提高B组模拟考试
今天,是一个重要的日子。
(想什么呢?我说的当然是那位大人的生日啦)
T1 题意简述:jzoj3223
解题思路:出题人居然已经懒到用河北2013年的省选题当考题了...
而且重点是还把HEOI打成HBOI(湖北OI)了...
这道题怎么做不用我多说了吧。
正反各做一遍背包,二进制拆分优化即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int n,k,ans,mon[1001],wei[1001],tim[1001]; int dp1[1001][1001],dp2[1001][1001]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d%d",&mon[i],&wei[i],&tim[i]); for(int i=1;i<=n;i++) { int lim=tim[i]; for(int j=0;j<=1000;j++) dp1[i][j]=dp1[i-1][j]; for(int j=1;lim;j=min(j<<1,lim)) { for(int l=1000;l>=j*mon[i];l--) dp1[i][l]=max(dp1[i][l],dp1[i][l-j*mon[i]]+j*wei[i]); lim-=j; } } for(int i=n;i>=1;i--) { int lim=tim[i]; for(int j=1;j<=1000;j++) dp2[i][j]=dp2[i+1][j]; for(int j=1;lim;j=min(j<<1,lim)) { for(int l=1000;l>=j*mon[i];l--) dp2[i][l]=max(dp2[i][l],dp2[i][l-j*mon[i]]+j*wei[i]); lim-=j; } } scanf("%d",&k); for(int i=1;i<=k;i++) { int u,v; scanf("%d%d",&u,&v); u++; ans=0; for(int j=0;j<=v;j++) ans=max(ans,dp1[u-1][j]+dp2[u+1][v-j]); printf("%d\n",ans); } return 0; }
T2 题意简述:jzoj100029
解题思路:思路有点绕的贪心。
首先挑出所有可能成为陪审团成员之一的人。
对Y最优的情况:yk最大的t个人都在备选行列内。
此时Y可以挑这t个yk最大的人。
对Y最劣的情况:yk最大的n-s个人都不在备选行列内。
此时Y只能挑yk排名从n-s+1到n-s+t这t个人。
因此yk值排名前n-s+t个人都有可能成为陪审团成员之一。
在第一次排序中若yk值相同,则按xk值从小到大排列。
因为Y的策略是保证yk值最大的前提下使xk值最小,因此若最后几个人yk值都相同,Y会挑xk
值小的,而xk值大的并没有被挑走的可能。
然后把这n-s+t个人按xk从大到小排序,我们要让排名前t个人成为陪审团。
让他们成为陪审团有一定条件:
备选队列里的其他人yk值必须严格小于陪审团成员yk值的最小值。
为什么不能等于?因为若yk值相等,Y就会挑xk值小的。除非xk,yk这两个值均相同,否则
陪审团总xk值就会变小。
为了让Y排除掉的那s-t个人yk值尽量大,我们需要让陪审团成员yk值尽量大。
因此第二次排序时若xk值相同,就按yk值从大到小排列。
接下来要从剩下的n-t个人中挑选那s-t个人。
因为要使Y排除掉的那s-t个人yk值尽量大,因此我们按yk值从大到小排序。
同时我们还要使备选成员的xk值总和尽量大,因此yk值相同时按xk值从大到小排列。
选出前s-t个人即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #define INF 0x3f3f3f3f #define ll long long using namespace std; ll n,s,t,cnt,ansx,ansy; struct uio{ ll vx,vy; }peo[100001],peo1[100001]; bool cmp1(uio x,uio y) { if(x.vy==y.vy) return x.vx<y.vx; return x.vy>y.vy; } bool cmp2(uio x,uio y) { if(x.vx==y.vx) return x.vy>y.vy; return x.vx>y.vx; } bool cmp3(uio x,uio y) { if(x.vy==y.vy) return x.vx>y.vx; return x.vy>y.vy; } int main() { scanf("%lld%lld%lld",&n,&s,&t); for(ll i=1;i<=n;i++) scanf("%lld%lld",&peo[i].vx,&peo[i].vy); sort(peo+1,peo+1+n,cmp1); sort(peo+1,peo+1+n-s+t,cmp2); ll mnx=INF,mny=INF,mxx=-1,mxy=-1; for(ll i=1;i<=t;i++) { ansx+=peo[i].vx,ansy+=peo[i].vy; mnx=min(mnx,peo[i].vx),mny=min(mny,peo[i].vy); mxx=max(mxx,peo[i].vx),mxy=max(mxy,peo[i].vy); } for(ll i=1;i<=n;i++) if(peo[i].vy<mny) peo1[++cnt]=peo[i]; sort(peo1+1,peo1+1+cnt,cmp3); for(ll i=1;i<=s-t;i++) ansx+=peo1[i].vx,ansy+=peo1[i].vy; printf("%lld %lld\n",ansx,ansy); return 0; }
T3 题意简述:jzoj4676
解题思路:一道KMP瞎搞的题,也可以用dp。这里介绍dp做法。
当然,字符串匹配的题要先把next数组求出来。
设dp[i]表示能完全覆盖前缀i的最短前缀的长度。
发现dp[i]最多只有2种取值:i或是dp[next[i]]。
因为能完全覆盖一个字符串a的字符串b必同时是a的前缀和后缀。
考虑何时dp[i]=dp[next[i]]。
显然,最后next[i]个字符是肯定要被一个长为next[i]的前缀覆盖的。
除去这next[i]个字符,前面的字符要用若干个前缀覆盖。
为保证完全覆盖,覆盖前i-next[i]个字符的最后一个前缀与覆盖从第i-next[i]+1个字符到
第i个字符的这个前缀必然是首尾相接或是有重合部分的。如下图所示:
因此只需在[i-next[i],i]区间内找有没有dp[j]=dp[next[i]]的。
有则dp[i]=dp[next[i]],否则dp[i]=i。
每次转移时都找一遍太慢了,只需开数组mx[i]记录dp[j]=i的最大的j即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int n,next[500001],dp[500001],mx[500001]; char s[500001]; void getnxt() { int num=0; for(int i=2;i<=n;i++) { while(num&&s[num+1]!=s[i]) num=next[num]; if(s[num+1]==s[i]) num++; next[i]=num; } } int main() { scanf("%s",s+1); n=strlen(s+1); getnxt(); for(int i=1;i<=n;i++) { if(mx[dp[next[i]]]>=i-next[i]) dp[i]=dp[next[i]]; else dp[i]=i; mx[dp[i]]=i; } printf("%d\n",dp[n]); return 0; }