Educational Codeforces Round 137 (Rated for Div. 2)(补D、E)
D:Problem with Random Tests暴力、数据随机:Mid
题意
给你一个0、1字符串,取两个子串并且对应的10进制数的或值最大,输出或运算后的二进制字符串。
数据随机:每一个位置上的1、0出现的概率均为1/2
思路
比赛时不知道数据随机有什么用,如果直接暴力感觉O(n2)就超时了
暴力:
- 为了使最后的数最大,就要使二进制字符串最长,于是其中一个字符串一定选:第一个1出现起的子串s1
- 为了使s1通过或运算变大,可以使s1的第一个0变为1的子串s2
- 于是暴力遍历s1的第一个1后面,第一个0前面的所有1(长度为s1.length-第一个0的位置)的字符串,求出或运算后的最大值
看似复杂度为O(n2),但是第一个1后面,第一个0前面所有的1的个数为50个的概率为(1/2)50(由于数据的随机性),所有最多遍历50下就可以确定答案了
ps:对于比较两个字符串表示的数的大小可以直接比较字符串的大小
代码
普通暴力
#include<bits/stdc++.h>
#define ll long long
using namespace std;
string del(string s){
int i=0;
while(s[i]=='0'&&i<s.length()-1)i++;
return s.substr(i);
}
int main(){
int n;
string s;
cin>>n>>s;
s=del(s);
int end=0;
for(int i=0;i<s.length();i++){
if(s[i]=='0'){
end=i;
break;
}
}
string ans=s;
for(int i=0;i<end;i++){
string temp=s;
for(int j=i;j<i+s.length()-end;j++){
if(s[j]=='1')temp[j-i+end]='1';
}
ans=max(ans,temp);
}
cout<<ans<<endl;
}
利用bitset(可以进行位运算方便点)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
string del(string s){
int i=0;
while(s[i]=='0'&&i<s.length()-1)i++;
return s.substr(i);
}
int main(){
int n;
string s;
cin>>n>>s;
s=del(s);
bitset<1000005>s1(s),s2(s);
string ans=s1.to_string();
for(int i=1;i<=50;i++){
ans=max(ans,(s1|(s2>>i)).to_string());
}
cout<<del(ans)<<endl;
}
E:FTL动态规划:Hard–
题意
两个发射塔分别可以发射伤害为p1,p2的激光,但是充能时间分别t1,t2
其要攻击血量为h,防御力为s的敌方,总伤害为P的激光可以队敌方造成P-s的血量伤害。问最少需要多少时间击败对方
数据范围:
![]()
思路
对于炮台,它有两种方式攻击:
- 只要有炮台充能好就发射
- 等两个炮台一起充能好发射
于是可以用dp来求解:dp[i]表示造成i伤害需要的最小时间
,dp[i]可从以下方面取最小值
- 最后一次单独发射炮台1:
dp[max(0,i-p1+s)]+t1
- 最后一次单独发射炮台2:
dp[max(0,i-p2+s)]+t2
- 最后一次一起发射两炮台:这时需要考虑前面单独发射炮台的情况
- 假设一共发射了j次炮台1:
- j-1次单独发射炮台1:伤害贡献
(j-1)*(p1-s)+(j*t1-t2)/t2*(p2-s)
(前半部分是炮台1的单独发射的贡献,后半部分是这段时间炮台2单独发射的贡献) - 最后一次同炮台2一起发射:伤害贡献
p1+p2-s
- 时间花费
dp[max(0,i-总伤害)+j*t1]
- j-1次单独发射炮台1:伤害贡献
- 假设一共发射了j次炮台2:
- j-1次单独发射炮台2:伤害贡献
(j-1)*(p2-s)+(j*t2-p1)/p1*(p1-s)
(前半部分是炮台2的单独发射的贡献,后半部分是这段时间炮台1单独发射的贡献) - 最后一次同炮台1一起发射:伤害贡献
p1+p2-s
- 时间花费
dp[max(0,i-总伤害)+j*t2]
- j-1次单独发射炮台2:伤害贡献
- 关于在一起发射炮台前单独发射几次炮台(j的值),可以通过暴力的方式遍历,因为每个炮台最少可以造成1点伤害,于是
for(j=0;j<=i;j++)遍历到i
- 假设一共发射了j次炮台1:
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5005;
ll p1,t1,p2,t2,h,s;
ll dp[maxn];
int main(){
cin>>p1>>t1>>p2>>t2>>h>>s;
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=h;i++){
dp[i]=min(dp[max(0ll,i-p1+s)]+t1,dp[max(0ll,i-p2+s)]+t2);
for(int j=0;j<=i;j++){
if(j*t1>=t2){
ll x=(j-1)*(p1-s)+(j*t1-t2)/t2*(p2-s)+p1+p2-s;
dp[i]=min(dp[i],dp[max(0ll,i-x)]+j*t1);
}
if(j*t2>=t1){
ll x=(j-1)*(p2-s)+(j*t2-t1)/t1*(p1-s)+p1+p2-s;
dp[i]=min(dp[i],dp[max(0ll,i-x)]+j*t2);
}
}
}
cout<<dp[h]<<endl;
}