2022CUST上学期训练赛8
A:Edit Distance构造、思维:Mid–
题意
给你一个01字符串,求另一个相同长度的字符串,使它两的编码距离>=len/2。
思路
- 当字符串中0与1的数量不相等时:
- 若1多,输出000000…
- 若0多,输出111111…
- 当字符串中0的个数和1的个数相等时:利用动态规划求编码距离的思想
- 当s[0]='1’时,输出0111111…
- 当s[0]='0’时,输出1000000…
- 原因:
- 因为第一位不同,那么s与t的编码距离edit(s[1…n],t[1…n])有以下三种情况:下列的后续状态很容易求出来
- 替换情况(替换第一位以致相同+后续的状态):edit(s[1…len],t[1…len])+1=len/2+1(后续状态=len/2)
- 插入情况(将t[0]插入到s的前面+后续状态):edit(s[0…len],t[1…len])+1=len/2+1(后续状态=len/2-1+1需要一个删除操作所以要+1)
- 删除情况(将s[0]删除+后续状态):edit(s[1…len],t[0…len])+1=len/2+1(后续状态=len/2-1+1需要一次删除操作所以要+1)
- 因为第一位不同,那么s与t的编码距离edit(s[1…n],t[1…n])有以下三种情况:下列的后续状态很容易求出来
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s;
cin >> s;
int num0 = 0, num1 = 0;
for (int i = 0; i < s.length(); i++)
{
if (s[i] == '0')
num0++;
else
num1++;
}
if (num1 > num0)
{
for (int i = 0; i < s.length(); i++)
cout << 0;
}
else if (num1 < num0)
{
for (int i = 0; i < s.length(); i++)
cout << 1;
}
else if (num1 == num0)
{
if (s[0] == '1')
{
cout << 0;
for (int i = 0; i < s.length() - 1; i++)
cout << 1;
}
else
{
cout << 1;
for (int i = 0; i < s.length() - 1; i++)
cout << 0;
}
}
}
Icy Land思维:Mid–
题意
'#‘表示干地,’.'表示冰地
如果在冰地上往一个方向走,就会一直走直到走到干地或者边缘
你可以将冰地改成干地,问最小修改次数使得不管在哪个地方开始都可以遍历所有的点
思路
首先,不管从哪个地方开始可以转换成从(1,1)开始,因为哪个地方开始都可以走到(1,1)。
接下来进行分类讨论
(n<=2&&m<=2)
那么不用修改(n==1&&m>2)||(n==2&&m>2)
:那么需要保证2到m-1每一列都有干地(n>2&&m==1)||(n>2&&m==2)
:那么需要保证2到n-1每一行都有干地- 若上述都不是:
- 需要保证
a[2...n-1][2...m-1]
全是干地 - 并且要保证四周至少有一个点可以"着路":就是
a[1][2...m-1],a[n][2...m-1],a[2...n-1][1],a[2...n-1][m]
中至少有一个干地,因为这样才能从四周走到中间的干地
- 需要保证
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=5005;
int n,m;
int ans;
char a[maxn][maxn];
void solve(){
if(n<=2&&m<=2){
cout<<0;
return;
}
ans=0;
if(n==1&&m>2){
for(int i=2;i<=m-1;i++)if(a[1][i]=='.')ans++;
cout<<ans;
return;
}
ans==0;
if(m==1&&n>2){
for(int i=2;i<=n-1;i++)if(a[i][1]=='.')ans++;
cout<<ans;
return;
}
ans=0;
if(n==2&&m>2){
for(int i=2;i<=m-1;i++)if(a[1][i]=='.'&&a[2][i]=='.')ans++;
cout<<ans;
return;
}
if(m==2&&n>2){
for(int i=2;i<=n-1;i++)if(a[i][1]=='.'&&a[i][2]=='.')ans++;
cout<<ans;
return;
}
ans=0;
for(int i=2;i<=n-1;i++){
for(int j=2;j<=m-1;j++){
if(a[i][j]=='.')ans++;
}
}
bool judge=false;
for(int i=2;i<=n-1;i++){
if(a[i][1]=='#'||a[i][m]=='#')judge=true;
}
for(int i=2;i<=m-1;i++){
if(a[1][i]=='#'||a[n][i]=='#')judge=true;
}
if(!judge)ans++;
cout<<ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)cin>>a[i][j];
}
solve();
}
Binary String贪心、思维:Mid–
题意
将一个前缀0的二进制字符串删除任意字符多次,使其的10进制表示法<=k。
问删除的最小次数,并且删除后的字符串不能含有前缀0
思路
假设给定字符串s2的10进制表示法我为s10
l e n = ∣ s 2 ∣ − ∣ k ∣ ,二进制位数差 len=|s_2|-|k|,二进制位数差 len=∣s2∣−∣k∣,二进制位数差
贪心:为了以尽可能少的删除次数使得s10尽可能小,就把第一个1后面的1逐个删除(注意第一个1不能删除,保证不会出现前缀0),直到|s2|与|k|的二进制位数相等或者1删完:
- 当1删完之后, ∣ s 2 ∣ > = ∣ k ∣ |s_2|>=|k| ∣s2∣>=∣k∣,此时s2=1000000…,那么结果就是在删除0直到二者二进制位数相等:共len次操作
- 否则,此时1为删完且
∣
s
2
∣
=
∣
k
∣
|s_2|=|k|
∣s2∣=∣k∣,那么就直接判断此时s10与k的大小
- 若s10>k,那么在删一位:共len+1次操作
- 若s10<=k,那么达成目的:共len次操作
代码
#include<bits/stdc++.h>
using namespace std;
int main(){
string s;
cin>>s;
int num0=0,num1=0;
for(int i=0;i<s.length();i++){
if(s[i]=='0')num0++;
else num1++;
}
if(num1>num0){
for(int i=0;i<s.length();i++)cout<<0;
}
else if(num1<num0){
for(int i=0;i<s.length();i++)cout<<1;
}
else if(num1==num0){
if(s[0]=='1'){
cout<<0;
for(int i=0;i<s.length()-1;i++)cout<<1;
}
else {
cout<<1;
for(int i=0;i<s.length()-1;i++)cout<<0;
}
}
}