cf #792 Div.1+2
文章目录
A. Digit Minimization
- 题意
题目大致含义:给你一个不含0的长整数,玩一个游戏,Alice先手且一定要进行这样一个操作——选两个不同位置互换,Bob后手且一定进行这样一个操作——把最后一位删除;当只剩一个数时游戏结束,问留下的这个数最小是多少
-
题解
看样例,贪一下,结论是当位数大于2时留下的一定是数之最小的那一位。证明如下:
-
如果是两位数
特殊,游戏只进行一轮,Alice交换两位,Bob删除个位,最后剩下的数是确定的——输入时的个位数
-
如果是三位数
先以一个样例来模拟,假设这三位数是123,而这三位数的6种排列全部都可以把1留到最后,故可以证得三位数时一定可以把最小的那一位留下来,故答案就是数值最小那位
-
如果是多位数
多位数其实可以看成三位数的扩展,把多位数分为3个部分——最小的那位数是一部分、另外随便分成两坨;那么问题又化成了第二种情况,故原结论得证
-
-
代码
#include <iostream>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--){
int n;
cin>>n;
if(n<100){//两位数则是处于个位的那个数
cout<<n%10<<'\n';
continue;
}
//2位数以上则是整数中数值最小的那位
int res=11;
while (n){
int x=n%10;
n/=10;
res=min(res,x);
}
cout<<res<<'\n';
}
return 0;
}
B. Z mod X = C
- 题意
给定一组a,b,c(a<b<c),找出任何一组符合下列式子的x,y,z
x
m
o
d
y
=
a
;
y
m
o
d
z
=
b
;
z
m
o
d
x
=
c
x\bmod y=a; y\bmod z=b;z\bmod x=c
xmody=a;ymodz=b;zmodx=c
-
题解
既然是任意一组解,如果能找到一个一定符合式子的计算方式,那么这组解就找到了。
最简单的是这样:直接另z=c,那么对于其他两个式子的不定方程倍数都取1,就很容易得到x=a+b+c,y=b+c
-
代码
#include <iostream>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--){
int a,b,c;
cin>>a>>b>>c;
int z=c;
int y=z+b;
int x=y+a;
cout<<x<<" "<<y<<" "<<z<<" "<<'\n';
}
return 0;
}
C. Column Swapping
-
题意
给定一个矩阵,问能否找到两列交换之后使得矩阵中每一行都非递减的,如果有就输出这两列的列号
-
题解
矩阵如果本来就符合不管。
如果本来不符合,对于某一行可以证明:如果这一行可以只通过一次交换就符合要求,那么这行就只有这两个位置是需要交换的,其他都是符合要求的。所以只需要把这行和排好序的这行找一下任何两个不同的位置交换一下,然后再检查是否符合就能得到这一行是否可以通过一次交换变成非递减数列。
所以思路大概是把原始的数组和排好序的数组都存一下,找某一行中任意不符合要求的两个列进行两列交换,再进行检查是否符合。
-
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
int main()
{
int t;
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
vector<vector<int>> a(n,vector<int>(m));//二维数组不爆空间,且速度快于push_back的方法
vector<vector<int>> b(n,vector<int>(m));
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
cin>>a[i][j],b[i][j]=a[i][j];
for (int i = 0; i < n; i ++ ) sort(b[i].begin(),b[i].end());//存排好序的矩阵
int c1=-1,c2=-1;//找两个不同的列
for (int i = 0; i < n; i ++ ){
for (int j = 0; j < m; j ++ ){
if(a[i][j]!=b[i][j]){
if(c1==-1)c1=j;
else if(c2==-1)c2=j;
}
}
if(c1!=-1)break;
}
if(c1==-1&&c2==-1){//如果矩阵本身符合则不需后续的交换和检查
cout<<"1 1"<<'\n';
continue;
}
for (int i = 0; i < n; i ++ )swap(a[i][c1],a[i][c2]);//交换这不同的两列
bool OK=true;
for (int i = 0; i < n; i ++ )//看每一列是否符合
if(!is_sorted(a[i].begin(),a[i].end())){//is_sorted函数牛牛
OK=false;
break;
}
if(OK)cout<<c1+1<<" "<<c2+1<<'\n';//注意题是从1计数的
else cout<<-1<<'\n';
}
return 0;
}
D. Traps
-
题意
有n个陷阱,每个陷阱伤害为a[i],并且有k次机会能跳过陷阱,跳过就不受任何伤害,但每跳过一个陷阱后面所有的陷阱伤害值都+1,问过掉所有陷阱受到的最小伤害为多少。
-
题解
首先,一定是跳过k个陷阱会有伤害最小值。证明:如果跳最后k个陷阱,那么伤害值就不算最后k个伤害并且也没有附加伤害,也就是说跳k个陷阱已经有这种保底的一定是更优解的方案了,而可能还存在其他比这种方案更优的跳k个陷阱的方案,所以一定跳k个陷阱。
既然确定跳过k个陷阱,那么就选收益最大的k个陷阱,收益(带符号)w[i]=(n-i)-a[i],把收益排序后重新计算伤害值。注意跳过陷阱后其附加伤害也没有,记得再减去多算的部分。
-
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--){
int n,k;
cin>>n>>k;
if(n<=k){//直接剪枝
string str;
getline(cin,str);getline(cin,str);
cout<<0<<'\n';
continue;
}
long long sum=0,x;
vector<int> a(n);//每个陷阱的收益
for (int i = 1; i <= n; i ++ ){
cin>>x;
a.push_back(n-i-x);
sum+=x;
}
sort(a.begin(),a.end());//排序
for (int i = 0; i < k; i ++ ){//跳过k个陷阱
sum+=a[i];
sum-=i;//减去重复算的附加伤害值
}
cout<<sum<<'\n';
}
return 0;
}