A. Password(组合数)
给出0~9中用不到的数字,密码是由四位数字组成,其中每种数字有两个,问可能存在的密码种类数。
思路:在用到的数字中两两互组,对于两个数字组队而言,有6种排列方式,乘起来即可。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N=1e5+5;
int t,n;
int a[N];
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>t;
while(t--){
std::cin>>n;
for(int i=1;i<=n;i++){
std::cin>>a[i];
}
ll ans=((9-n)+1)*(9-n)/2*6;
std::cout<<ans<<'\n';
}
return 0;
}
B. Permutation Value(构造)
给出一个数字n,对于一个permutation,它的value是这个permutation中permutation的数量。对于每个n,构造一个value最小的permutation。
思路:正着放一半,倒着放一半即可,即对于5而言,可以是1 5 2 4 3。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N=1e5+5;
int t,n;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>t;
while(t--){
std::cin>>n;
std::vector<int>ans;
for(int i=1;i<=(n+1)/2;i++){
ans.push_back(i);
if((n+1)!=2*i)
ans.push_back(n+1-i);
}
for(int i=0;i<n;i++){
std::cout<<ans[i]<<" \n"[i==n-1];
}
}
return 0;
}
os:有简单的方法。。直接把1移到n后面就可以,,这样显得我很呆欸
C. Save the Magazines(DP,思维)
给出一个数组和一个对应长度的字符串,如果某个位置对应字符为1,则加上该位置数组对应的值,现在对于每一个1来说,它们都可以最多向前移动一次,问经过若干次操作之后,能得到的最大价值之和是多少。
思路: (1)比较好想的DP。我们可以开两维数组,第一维表示遍历到的位置,第二维表示该位置的状态,对于该位置是1的时候,可以有两种转换方式,f[i][0]是表示该位置的1向前移位,就一种情况,就是前一位也是0的情况下可以完成;f[i][1]的时候表示该位置的1不移动,此时取前一位的较大值,即max(f[i-1][1],f[i-1][0])+a[i]。对于该位是0时,只能存在f[i][0]的情况,直接从前一位的较大值转移即可。
(2)贪心。对于一段连续的1,我们可以枚举对应范围内最适合放0的位置,就是a[i]值最小的位置,然后扫一遍非0的位置加入答案中。
AC Code:
(1)
#include <bits/stdc++.h>
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=2e5+5;
int t,n;
std::string s;
int a[N];
ll f[N][2];
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>t;
while(t--){
std::cin>>n;
std::cin>>s;
s=' '+s;
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++){
std::cin>>a[i];
}
for(int i=1;i<=n;i++){
if(s[i]=='1'){
f[i][1]=std::max(f[i-1][0],f[i-1][1])+a[i];
f[i][0]=f[i-1][0]+a[i-1];
}
else f[i][0]=std::max(f[i-1][0],f[i-1][1]);
}
std::cout<<std::max(f[n][1],f[n][0])<<'\n';
}
return 0;
}
(2)
#include <bits/stdc++.h>
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=2e5+5;
int t,n;
std::string s;
int a[N];
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>t;
while(t--){
std::cin>>n;
std::cin>>s;
s=' '+s;
for(int i=1;i<=n;i++){
std::cin>>a[i];
}
int j=-1;
for(int i=1;i<=n;i++){
if(s[i]=='0') j=i;
else if(j!=-1&&a[j]>a[i]){
std::swap(s[i],s[j]);
j=i;
}
}
ll ans=0;
for(int i=1;i<=n;i++){
if(s[i]=='1')
ans+=a[i];
}
std::cout<<ans<<'\n';
}
return 0;
}
os:方法二我一开始用双指针写挂了。。。学习了jls的代码,感觉非常妙
D. Problem with Random Tests(暴力枚举,复杂度分析)
给出一个长度为n的01串,选择它的两个子串,使得它们所表示的十进制数的或值最大,输出这个最大的或值。
思路:显然,两个字符串中必定有一个是原字符串,因为原字符串表示的一定是子串中最大的数;对于答案,我们尽可能选择子串,令第一个1之后的最高位0变成1,因为如果某一位对于0有作用,必须选择的子串要大于等于最高位0的位数,所以字符串的开头一定位于最高位0到第一个1之间,暴力即可,至于暴力复杂度能过的原因,是因为01完全随机出现,全为50%的出现概率,所以直接暴力枚举复杂度能过。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
#define INF 0x3f3f3f3f
const int N=2e5+5;
int n;
std::string s;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>n>>s;
if(s.find('1')==std::string::npos){
std::cout<<0<<'\n';
return 0;
}
int a=s.find('1');
if(s.substr(a).find('0')==std::string::npos){
std::cout<<s.substr(a)<<'\n';
return 0;
}
int b=a+s.substr(a).find('0');
auto ans=s;
for(int i=0;i<=b-a;i++){
auto res=s;
for(int j=0;j+i<n;j++){
res[j+i]|=s[j];
}
ans=std::max(ans,res);
}
ans=ans.substr(ans.find('1'));
std::cout<<ans<<'\n';
return 0;
}
补不动辣!