太菜了,卡在C上以至于没有看到D。
A. Marketing Scheme
题意:如果 x mod a ≥ ⌊a/2⌋,那么这是好的。问在x∈[l,r]时,能否找到一个a,使得对于区间内任意的x都是好的。
对于区间左端点,a能取得的最大的情况,就是a=2*l了,那么只要r小于2*l就成立。
B. Reverse Binary Strings
题意:翻转01串,使之变成01交题串
两种理解:
1、翻转一次不会造成内部的变化,只改变首尾两点。那么我们找多少个需要被交换的0或者1就行了。
比如(111)那么就有两个1需要被翻转走,需要被交换的01个数取大就好。
int cnt1 = 0, cnt2 = 0; for(int i=1;i<s.length();++i)( { if(s[i]==s[i-1]) { if(s[i]=='1') cnt1++; else cnt2++; } } cout<<max(cnt2,cnt1)<<endl;
2、目标串只有101010以及010101的形式,所以,既然翻转一次内部方向改变,内部交替不变,那么我再换一次内部就好了。
比如11101000的目标是10101010,那么只有2和6号位不匹配,那么我翻转2-6,再翻转3-5就能达成目标。
对于目标是101010,但当中的四位0101都不匹配,那么我只用翻转一次就好。
这时候就会有一个问题?当前串01010不匹配呢,翻转也不行的啊?考虑到其他地方会多一个1不匹配,所以不计也没关系。
那么如果一定要多次操作才能还原后面的1呢?那这时候目标串是010101不是更好吗。
证明不是很严谨,当时是看到队友a了,急中生智,看了一下跟别人的解法都不一样。
可能是错的。
memset(vis,0,sizeof(vis)); ans=anss=0; scanf("%d",&n); scanf("%s",a+1); //10101010 f=1; for(int i=1;i<=n;++i){ if(a[i]-'0'==f){ } else{ ans++; if(!vis[i]) vis[i]=1; //需要被交换 if(vis[i-1]) ans--; //前面一位也被交换,当前位不计贡献 } f^=1; } memset(vis,0,sizeof(vis)); f=0; for(int i=1;i<=n;++i){ if(a[i]-'0'==f){ } else{ anss++; if(!vis[i]) vis[i]=1; if(vis[i-1]) anss--; } f^=1; } ans=min(ans,anss); printf("%d ",ans);
C. Chef Monocarp
题意:一分钟只能出一个餐,一开始所有餐都在烤炉里,每次出餐贡献+|t[i]−T|(T为当前时间),问让总贡献最小的方法。
(注:出餐时间不一定连续。
很显然是一个dp题,但是我没想到方程怎么写。
dp[i][j]表示第i号餐在j时间出来的最优解。
先排个序,因为时间是线性增长的,t[i]线性增长更优。
代码来自一位dalao
int dp[270][270*2],a[270],t,n; int main(){ scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); memset(dp,0x7f,sizeof(dp)); for(int i=0;i<=2*n;++i) dp[0][i]=0; sort(a+1,a+1+n); for(int i=1;i<=n;++i){ for(int j=i-1;j<=2*n;++j){ for(int k=j+1;k<=2*n;++k){ dp[i][k]=min(dp[i][k],dp[i-1][j]+abs(a[i]-k)); } } } int ans=inf; for(int i=n;i<=2*n;++i) ans=min(ans,dp[n][i]); printf("%d ",ans); } return 0; }
D. Minimal Height Tree
题意:给定树的bfs序,对任意节点的子节点按升序排列,问最小高度是多少?
如果理解不了,我就来画个图吧。
两个都是1 4 5 2 3的遍历顺序,明显第一幅图更优。
先看一下代码
int t,a[200007],q[200007],p,n; //q记录每层的个数 int main(){ scanf("%d",&t); while(t--){ scanf("%d",&n); memset(q,0,sizeof(q)); q[0]=1;p=1; for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=2;i<=n;++i){ if(a[i]>a[i-1]){ //升序就加入当前层 q[p]++; } else if(q[p-1]>1){ //看一下上一层子结点个数大于1,挂到上层那个点 q[p-1]--; q[p]++; } else if(q[p-1]==1){ //上层个数==1,说明,上层已经没有空间加了 p++; q[p]++; } } printf("%d ",p); } return 0; }
还理解不了就对图跑一遍,记住节点的子结点都必须升序。
E. Make It Increasing
感谢该博主https://www.cnblogs.com/werner-yin/p/solution-CF-1437E.html的题解。
以下会对该博主的代码加上自己的注释和理解,便于观看。建议点开双开。
#include<cstdio> #include<cmath> #include<iostream> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<vector> #include<set> #include<iomanip> #define inf 0x3f3f3f3f #define ll long long using namespace std; const int N= 1e6+7; int a[N],b[N],lock[N],d[N],last=0,pos=0,n,k; int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); } for(int i=1;i<=k;++i){ scanf("%d",&b[i]); lock[b[i]]=1; } sort(b+1,b+1+k); for(int i=1;i<k;++i) if(a[b[i]]-b[i]>a[b[i+1]]-b[i+1]){ //保证后一位锁定位之差大于前一位锁定位之差 printf("-1 "); //因为严格上升,且上升的差值大于等于1 return 0; //比如三号位和六号位被锁,那么4 6的值是不合法的 } /* 要求的是改几个位子使之成为严格上升序列。 按一般的思路,找一下最长的严格上升子序列。 原长度-找到的长度就行。 但是这样有一个问题,我们不能保证找的子序列中间能改出来。 比如样例一,我们能找到1 2 3 5.但是 2 3 之间的两个1 我们没有办法。 所以我们先减去当前位的值。使之满足严格 然后找最长不下降就行了。 */ for(int i=1;i<=n;++i) a[i]-=i; //保证严格上升,且上升的差值大于等于1 for(int i=1;i<=n;++i){ if(pos==0||a[i]>=d[pos]){ d[++pos]=a[i]; if(lock[i]) last=pos; //如果新加入的点是锁定位,标记他 } else{ int p= upper_bound(d+1,d+1+pos,a[i])-d; if(p<=last) continue; //如果找到替换位置在最后一个锁定位之前 d[p]=a[i]; //那么如果更换了,锁定就消失了。所以不能更换。 if(lock[i]) last=p,pos=p; } } printf("%d ",n-pos); return 0; }