A题
'c’本就弱小(tzcoder8390)
思路
水题
#include<bits/stdc++.h>
using namespace std;
int main(){
double a,b,c;
cin>>a>>b>>c;
if(c+a>b&&c+b>a) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}
B题
XOR!(tzcoder8391)
思路
模拟题,难点在于怎么处理中间的符号以及切割出整数值
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
string s;
cin>>s;
int cas;//中间的运算符是什么
int num_left=0,num_right=0;//num_left是式子左侧的值,num_right是式子右侧的值
int now=0;//当前切割的数字
for(int i=0;i<s.size();i++){
if(s[i]=='^'){
num_left^=now;
now=0;
}
else if(s[i]=='+'){
num_right+=now;
now=0;
}
else if(s[i]>='0'&&s[i]<='9'){
now=now*10+s[i]-'0';
}
else{//此时必定是中间的运算符
num_left^=now;//注意处理最后一个整数
now=0;
//注意用if写的话,情况3要写在情况0前,情况4要写在情况1前面
if(s[i]=='<'&&s[i+1]=='=') cas=3,i++;
else if(s[i]=='>'&&s[i+1]=='=') cas=4,i++;
else if(s[i]=='!') cas=5,i++;//注意这里下标多+1,否则会和后面的=情况重复
else if(s[i]=='<') cas=0;
else if(s[i]=='>') cas=1;
else if(s[i]=='=') cas=2;
}
}
num_right+=now;//右侧的最后一个整数不要忘记处理
int flag;
switch(cas){
case 0:flag=num_left<num_right;break;
case 1:flag=num_left>num_right;break;
case 2:flag=num_left==num_right;break;
case 3:flag=num_left<=num_right;break;
case 4:flag=num_left>=num_right;break;
case 5:flag=num_left!=num_right;break;
}
if(flag) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}
C题
乐乐的强迫症(tzcoder8392)
思路
01
01
01背包模型
从
a
a
a到
b
b
b,其实就是能不能凑出
b
−
a
b-a
b−a的分数。
把分值看做容量,这就是一道
01
01
01背包的经典模型。
注意特判
a
>
b
a>b
a>b的情况
#include<bits/stdc++.h>
using namespace std;
int dp[40007];//dp[i]代表凑出分数i的最少题目数量
int main(){
int n,a,b;
cin>>n>>a>>b;
if(a>b){
cout<<-1<<endl;
return 0;
}
dp[0]=0;//初始情况,0分是可以凑出来的
int lim=b-a;
for(int i=1;i<=lim;i++) dp[i]=1e9;
while(n--){
int x;
cin>>x;
for(int i=lim;i>=x;i--){
dp[i]=min(dp[i],dp[i-x]+1);
}
}
if(dp[lim]==1e9) cout<<-1<<endl;
else cout<<dp[lim]<<endl;
}
D题
maomeng的数学魔鬼训练(tzcoder8393)
思路
贪心
从直觉上,我们肯定要把耗时最长的书和耗时最少的游戏安排在同一天
那么如果我们把书按照耗时从大到小排序,把游戏按照耗时从小到大排序,是否这样的顺序就一定是最优的呢?
答案是必然的,证明如下
比如我们尝试对第
i
i
i多耗时的书(定义为
x
[
i
]
x[i]
x[i])和第
i
i
i少耗时的游戏(定义为
y
[
i
]
y[i]
y[i])安排在同一天
如果
x
[
i
]
+
y
[
i
]
>
24
x[i]+y[i]>24
x[i]+y[i]>24,则无法安排
那么我们尝试,肯定要把
x
[
i
]
x[i]
x[i]或者
y
[
i
]
y[i]
y[i]中的一个尝试缩小
如果我们尝试缩小
y
[
i
]
y[i]
y[i],也就是说要取一个
j
<
i
j<i
j<i,尝试用
y
[
j
]
y[j]
y[j]替代
y
[
i
]
y[i]
y[i]
那么即使我们可以使得
x
[
i
]
+
y
[
j
]
<
=
24
x[i]+y[j]<=24
x[i]+y[j]<=24成立,但是造成的影响
x
[
j
]
+
y
[
i
]
x[j]+y[i]
x[j]+y[i]必定是
>
24
>24
>24的,因为
x
[
j
]
>
x
[
i
]
x[j]>x[i]
x[j]>x[i]
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
int a[1007],b[1007];
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++) cin>>b[i];
sort(a,a+n);
sort(b,b+n);
for(int i=0;i<n;i++){
if(a[i]+b[n-i-1]>24){
cout<<"NO"<<endl;
return 0;
}
}
cout<<"YES"<<endl;
}
E题
##回文自动机(tzcoder8394)
思路
d
p
+
dp+
dp+矩阵快速幂加速
需要借助哈希计算
见注释
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=2e5+7;
const int mod=1000000007;
string s;
long long hash_pre[maxn*2],hash_suf[maxn*2];
long long ans[2];//ans[0]代表回文的情况数,ans[1]代表不回文的情况数
long long mat[2][2];//关系转移矩阵
long long temp_base=1;//截取下来的一半字符串最高位+1对应的权值
int cnt=0;
void mul_mat(long long a[2][2],long long b[2][2]){
//计算矩阵a*矩阵b
long long c[2][2];
memset(c,0,sizeof(c));
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
for(int k=0;k<2;k++){
c[i][j]=(a[i][k]*b[k][j]%mod+c[i][j])%mod;
}
}
}
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
a[i][j]=c[i][j];
}
}
}
void qpow(long long a[2][2],long long p){
long long ans[2][2];
ans[0][0]=ans[1][1]=1;
ans[0][1]=ans[1][0]=0;
while(p){
if(p&1) mul_mat(ans,a);
mul_mat(a,a);
p>>=1;
}
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
a[i][j]=ans[i][j];
}
}
}
int main(){
string s;
long long k;
cin>>s>>k;
int len=s.size();
s=s+s;
for(int i=0;i<len*2;i++) hash_pre[i+1]=(hash_pre[i]*10%mod+s[i]-'a')%mod;
for(int i=len*2-1;i>=0;i--) hash_suf[i+1]=(hash_suf[i+2]*10%mod+s[i]-'a')%mod;
for(int i=0;i<len/2;i++) temp_base=temp_base*10%mod;
// for(int i=1;i<=len*2;i++){
// cout<<hash_pre[i]<<' '<<hash_suf[i]<<' '<<i<<endl;
// }
for(int i=1;i<=len;i++){
//以第i个字符作为字符串开头的情况
long long hash1,hash2;
if(len&1){
//奇数情况下,第一段是[i,i+len/2-1],第二段是[i+len/2+1,i+len-1]
hash1=(hash_pre[i+len/2-1]-hash_pre[i-1]*temp_base%mod+mod)%mod;
hash2=(hash_suf[i+len/2+1]-hash_suf[i+len]*temp_base%mod+mod)%mod;
//cout<<hash1<<' '<<hash2<<' '<<i<<'!'<<endl;
}
else{
//偶数情况下,第一段是[i,i+len/2-1],第二段是[i+len/2,i+len-1]
hash1=(hash_pre[i+len/2-1]-hash_pre[i-1]*temp_base%mod+mod)%mod;
hash2=(hash_suf[i+len/2]-hash_suf[i+len]*temp_base%mod+mod)%mod;
}
if(hash1==hash2){
cnt++;
}
if(i==1){
if(hash1==hash2) ans[0]=1;
else ans[1]=1;
}
}
mat[0][0]=cnt-1;//从回文转移到回文有cnt-1种
mat[1][0]=cnt;//从非回文转移到回文有cnt种
mat[0][1]=len-cnt;//从回文转移到非回文有len-cnt种
mat[1][1]=len-cnt-1;//从非回文转移到非回文有len-cnt-1种
// for(int i=0;i<2;i++){
// for(int j=0;j<2;j++){
// cout<<mat[i][j]<<' ';
// }
// cout<<endl;
// }
qpow(mat,k);
// for(int i=0;i<2;i++){
// for(int j=0;j<2;j++){
// cout<<mat[i][j]<<' ';
// }
// cout<<endl;
// }
//最后计算ans矩阵乘以mat矩阵即可
//ans[0]就是答案
cout<<(ans[0]*mat[0][0]%mod+ans[1]*mat[1][0]%mod)%mod<<endl;
}
F题
狭路相逢勇者胜!(tzcoder8395)
思路
思维结论,无算法
需要先能观察推导出,当前这一轮的第i位选手,如果他获胜晋级,那么他在下一轮会是第
i
/
2
i/2
i/2位选手。
在此基础上,再想到什么时候两个人会相遇,即两个人的序号差值为 1 1 1,并且小的那名选手的序号是偶数。
因此不断循环模拟晋级过程即可,一直到两个人相遇。复杂度 l o g ( n ) log(n) log(n)
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,a,b;
cin>>n>>a>>b;
if(a>b) swap(a,b);//注意题目没说a>b,不交换会导致下面的循环死循环
int ans=1;
while(a%2||b!=a+1){
a>>=1;
b>>=1;
ans++;
}
cout<<ans<<endl;
}
G题
“TZOJ”(tzcoder8396)
思路
此题可以
n
2
n^2
n2复杂度枚举每个点,作为我们选择的正方形区域的最左上角。
然后如果直接
n
n
n复杂度枚举正方形区域的边长,借助二维前缀和
O
(
1
)
O(1)
O(1)计算区域内的T,Z,O,J的个数。
这种写法的复杂度是
n
3
n^3
n3,会超时。
容易注意到,对于左上角固定某个位置的情况下,边长越长,越可能满足包含T,Z,O,J的这个条件,也就是存在一个值x,当边长>=x时,正方形区域都满足要求,也就是满足二分性。
因此我们可以二分正方形的边长,将复杂度降低到
O
(
n
2
∗
l
o
g
n
)
O(n^2*logn)
O(n2∗logn)
不过这里还可以继续优化
我们以
[
i
]
[
j
]
[i][j]
[i][j]作为正方形的左上角时,其实对于所有的
i
−
j
=
k
,
k
i-j=k,k
i−j=k,k是一个常数,也就是说对于从左上角斜向右下角的斜
45
45
45度线上的所有矩形,我们是可以放在一起讨论的。
如果说从这个“线性”角度思考,很容易发现是满足双指针移动方向的单调性。
即以
[
i
]
[
j
]
[i][j]
[i][j]作为左上角,
[
t
o
i
]
[
t
o
j
]
[toi][toj]
[toi][toj]作为正方形区域刚好包含T,Z,O,J时的右下角
对于
i
−
j
=
k
,
k
i-j=k,k
i−j=k,k是一个常数时,明显
i
i
i和
j
j
j越大,
t
o
i
toi
toi和
t
o
j
toj
toj也越大。
因此对于每个斜线情况,我们可以采用双指针的策略,将整体复杂度降低到
O
(
n
2
)
O(n^2)
O(n2)
注意此时我们枚举矩阵左上角位置的顺序,也应该变成斜
45
45
45度方向
同时双指针的写法由于每次长度只扩展
1
1
1,因此可以只使用一维前缀和,当然使用二维前缀和代码会更简洁和容易理解。
下方代码写的是一维前缀和。
#include<bits/stdc++.h>
using namespace std;
int n,m;
char field[1007][1007];
struct Node{
int T,Z,O,J;
}sum[2][1007][1007];
//sum[0]是横向的前缀和
//sum[1]是竖向的前缀和
int main(){
memset(sum,0,sizeof(sum));
cin>>n>>m;
for(int i=0;i<n;i++) cin>>field[i];
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(field[i][j]=='T') sum[0][i+1][j+1].T=sum[1][i+1][j+1].T=1;
else if(field[i][j]=='Z') sum[0][i+1][j+1].Z=sum[1][i+1][j+1].Z=1;
else if(field[i][j]=='O') sum[0][i+1][j+1].O=sum[1][i+1][j+1].O=1;
else if(field[i][j]=='J') sum[0][i+1][j+1].J=sum[1][i+1][j+1].J=1;
}
}
//做横向前缀和
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
sum[0][i][j].T+=sum[0][i][j-1].T;
sum[0][i][j].Z+=sum[0][i][j-1].Z;
sum[0][i][j].O+=sum[0][i][j-1].O;
sum[0][i][j].J+=sum[0][i][j-1].J;
}
}
//做竖向前缀和
for(int j=1;j<=m;j++){
for(int i=1;i<=n;i++){
sum[1][i][j].T+=sum[1][i-1][j].T;
sum[1][i][j].Z+=sum[1][i-1][j].Z;
sum[1][i][j].O+=sum[1][i-1][j].O;
sum[1][i][j].J+=sum[1][i-1][j].J;
}
}
int ans=0;
//以field[nowi][nowj]作为我们正方形区域的左上角,共n^2种情况,借助双指针思想,整体复杂度仍为O(n^2)
//先计算整个矩形右上角一侧的点作为正方形区域的左上角的情况
for(int j=1;j<=m;j++){
int nowi=1,nowj=j;//field[nowi][nowj]作为正方形区域的左上角
int to_i=nowi,to_j=nowj;//field[to_i][to_j]作为正方形区域的右下角
int T=sum[0][nowi][nowj].T-sum[0][nowi][nowj-1].T;
int Z=sum[0][nowi][nowj].Z-sum[0][nowi][nowj-1].Z;
int O=sum[0][nowi][nowj].O-sum[0][nowi][nowj-1].O;
int J=sum[0][nowi][nowj].J-sum[0][nowi][nowj-1].J;
//T,Z,O,J统计当前正方形区域中对应字符的个数
while(nowi<=n&&nowj<=m){
while(1){
if(T&&Z&&O&&J) break;//当前边长足够长,包含了四种字符
if(to_i==n||to_j==m) break;//右下角已经无法再往右下方向拓展
//右侧增加一列,对应下标field[nowi][to_j+1]到field[to_i+1][to_j+1]
T+=sum[1][to_i+1][to_j+1].T-sum[1][nowi-1][to_j+1].T;
Z+=sum[1][to_i+1][to_j+1].Z-sum[1][nowi-1][to_j+1].Z;
O+=sum[1][to_i+1][to_j+1].O-sum[1][nowi-1][to_j+1].O;
J+=sum[1][to_i+1][to_j+1].J-sum[1][nowi-1][to_j+1].J;
//底部增加一行,对应下标field[to_i+1][nowj]到field[to_i+1][to_j]//这里没有+1,右下角那个点别算重复了
T+=sum[0][to_i+1][to_j].T-sum[0][to_i+1][nowj-1].T;
Z+=sum[0][to_i+1][to_j].Z-sum[0][to_i+1][nowj-1].Z;
O+=sum[0][to_i+1][to_j].O-sum[0][to_i+1][nowj-1].O;
J+=sum[0][to_i+1][to_j].J-sum[0][to_i+1][nowj-1].J;
to_i++;
to_j++;
//右下角往右下尝试拓展长度1
}
if(T&&Z&&O&&J) ans+=min(n-to_i+1,m-to_j+1);//比当前右下角更右下的位置都满足答案
//左上角尝试往右下挪动距离1的位置
//上侧减少一行,对应下标field[nowi][nowj]到field[nowi][to_j]
T-=sum[0][nowi][to_j].T-sum[0][nowi][nowj-1].T;
Z-=sum[0][nowi][to_j].Z-sum[0][nowi][nowj-1].Z;
O-=sum[0][nowi][to_j].O-sum[0][nowi][nowj-1].O;
J-=sum[0][nowi][to_j].J-sum[0][nowi][nowj-1].J;
//左侧减少一列,对应下标field[i+1][nowj]到field[to_i][nowj] //这里也一样别把左上角的点多算了一次
T-=sum[1][to_i][nowj].T-sum[1][nowi][nowj].T;
Z-=sum[1][to_i][nowj].Z-sum[1][nowi][nowj].Z;
O-=sum[1][to_i][nowj].O-sum[1][nowi][nowj].O;
J-=sum[1][to_i][nowj].J-sum[1][nowi][nowj].J;
nowi++;
nowj++;
}
}
//再计算整个矩形左下角一侧的点作为正方形区域的左上角的情况
for(int i=2;i<=n;i++){
int nowi=i,nowj=1;
int to_i=nowi,to_j=nowj;//field[to_i][to_j]作为正方形区域的右下角
int T=sum[0][nowi][nowj].T-sum[0][nowi][nowj-1].T;
int Z=sum[0][nowi][nowj].Z-sum[0][nowi][nowj-1].Z;
int O=sum[0][nowi][nowj].O-sum[0][nowi][nowj-1].O;
int J=sum[0][nowi][nowj].J-sum[0][nowi][nowj-1].J;
//T,Z,O,J统计当前正方形区域中对应字符的个数
while(nowi<=n&&nowj<=m){
while(1){
if(T&&Z&&O&&J) break;//当前边长足够长,包含了四种字符
if(to_i==n||to_j==m) break;//右下角已经无法再往右下方向拓展
//右侧增加一列,对应下标field[nowi][to_j+1]到field[to_i+1][to_j+1]
T+=sum[1][to_i+1][to_j+1].T-sum[1][nowi-1][to_j+1].T;
Z+=sum[1][to_i+1][to_j+1].Z-sum[1][nowi-1][to_j+1].Z;
O+=sum[1][to_i+1][to_j+1].O-sum[1][nowi-1][to_j+1].O;
J+=sum[1][to_i+1][to_j+1].J-sum[1][nowi-1][to_j+1].J;
//底部增加一行,对应下标field[to_i+1][nowj]到field[to_i+1][to_j]//这里没有+1,右下角那个点别算重复了
T+=sum[0][to_i+1][to_j].T-sum[0][to_i+1][nowj-1].T;
Z+=sum[0][to_i+1][to_j].Z-sum[0][to_i+1][nowj-1].Z;
O+=sum[0][to_i+1][to_j].O-sum[0][to_i+1][nowj-1].O;
J+=sum[0][to_i+1][to_j].J-sum[0][to_i+1][nowj-1].J;
to_i++;
to_j++;
//右下角往右下尝试拓展长度1
}
if(T&&Z&&O&&J) ans+=min(n-to_i+1,m-to_j+1);//比当前右下角更右下的位置都满足答案
//左上角尝试往右下挪动距离1的位置
//上侧减少一行,对应下标field[nowi][j]到field[nowi][to_j]
T-=sum[0][nowi][to_j].T-sum[0][nowi][nowj-1].T;
Z-=sum[0][nowi][to_j].Z-sum[0][nowi][nowj-1].Z;
O-=sum[0][nowi][to_j].O-sum[0][nowi][nowj-1].O;
J-=sum[0][nowi][to_j].J-sum[0][nowi][nowj-1].J;
//左侧减少一列,对应下标field[nowi+1][nowj]到field[to_i][nowj] //这里也一样别把左上角的点多算了一次
T-=sum[1][to_i][nowj].T-sum[1][nowi][nowj].T;
Z-=sum[1][to_i][nowj].Z-sum[1][nowi][nowj].Z;
O-=sum[1][to_i][nowj].O-sum[1][nowi][nowj].O;
J-=sum[1][to_i][nowj].J-sum[1][nowi][nowj].J;
nowi++;
nowj++;
}
}
cout<<ans<<endl;
}
H题
佳加简减(tzcoder8397)
思路
看起来吓人,其实很简单
非严格上升子序列的长度
+
+
+非严格下降子序列的长度尽可能大
非严格上升子序列的最大长度是
n
n
n
非严格下降子序列的最大长度是
n
n
n
能不能两个同时取
n
n
n?
可以,整个数列里的数都相等就可以。
那么这道题就变成了把整个数组里的所有数变成相同的同一个数,最少的操作次数
这就是一个经典的中位数的题目了
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
int num[maxn];
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&num[i]);
sort(num,num+n);
long long ans=0;
int base=num[n/2];
for(int i=0;i<n;i++) ans+=abs(num[i]-base);
printf("%lld\n",ans);
}
I题
Auuuu的水题(tzcoder8398)
思路
二维平面上的多源bfs
直接把所有初始为水源的点压到队列里,统计每个非水源点周围水源点的个数。
每次从队列里弹出一个水源位置,更新周围非水源点记录的水源点个数信息,一旦有
2
2
2个以上水源就把这个位置修改为水源,同时压入队列中等待更新周边位置。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=507;
int n,m;
char field[maxn][maxn];
int cnt[maxn][maxn];//cnt[i][j]代表(i,j)这个位置周围有几个点是水源
struct Node{
int x,y;
};
queue<Node>Q;
int dir[4][2]={//对应朝着四个方向走
1,0,
-1,0,
0,1,
0,-1
};
int main(){
cin>>n>>m;
for(int i=0;i<n;i++) cin>>field[i];
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(field[i][j]=='*') Q.push({i,j});
}
}
while(Q.size()){
Node now=Q.front();
Q.pop();
for(int i=0;i<4;i++){
int nextx=now.x+dir[i][0];
int nexty=now.y+dir[i][1];
if(nextx>=0&&nextx<n&&nexty>=0&&nexty<m){//保证在地图内
if(field[nextx][nexty]=='.'){
cnt[nextx][nexty]++;//周围水源点个数+1
if(cnt[nextx][nexty]==2){//超过两个点
Q.push({nextx,nexty});
field[nextx][nexty]='*';//当前位置变为水源点
}
}
}
}
}
for(int i=0;i<n;i++) cout<<field[i]<<endl;
}
J题
Auuuu的粥游账号(tzcoder8399)
思路
dfs暴力枚举所有情况的经典题,同时兼备字典序
必须学会
后期骗分必备写法
#include<bits/stdc++.h>
using namespace std;
int n;
bool flag[27];//记录每个数有没有被使用过
struct Node{
int num[12];
};
vector<Node>out;
Node temp;
void dfs(int deep,int x){//x代表前面用过的数最大是多少
if(deep==12){
out.push_back(temp);
return;
}
//deep代表当前暴力枚举第deep个数放什么
for(int j=x+1;j<=n;j++){
//注意从小到大枚举,这样就可以保证字典序
if(flag[j]) continue;//被用过了就跳过
flag[j]=1;//标记为被使用过
temp.num[deep]=j;
dfs(deep+1,j);
flag[j]=0;//记得还原
}
}
int main(){
cin>>n;
dfs(0,0);
cout<<out.size()<<endl;
for(int i=0;i<out.size();i++){
for(int j=0;j<12;j++){
if(j) cout<<' ';
cout<<out[i].num[j];
}
cout<<endl;
}
}
K题
上课不要讲话 (tzcoder8400)
思路
可以把第一个人能坐的所有位置的周围位置都打上标记
然后监测第二个人能不能坐在打上标记的位置上即可
注意不要忘记考虑两个人初始位置也是一种选择
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=107;
int n,m,a,b;
bool flag[maxn][maxn];
//flag[i][j]代表第i行第j列这个位置能不能与Richadal相邻
int main(){
cin>>n>>m>>a>>b;
flag[0][1]=flag[1][0]=flag[1][1]=1;//别忘了初始位置
while(a--){
int temp;
cin>>temp;
//temp对应在第(temp-1)/m行,第(temp-1)%m列
int nowr=(temp-1)/m,nowc=(temp-1)%m;
for(int i=-1;i<=1;i++){
for(int j=-1;j<=1;j++){//枚举周围8个格子
if(i==0&&j==0) continue;
if(nowr+i>=0&&nowr+i<n&&nowc+j>=0&&nowc+j<m) flag[nowr+i][nowc+j]=1;
}
}
}
while(b--){
int temp;
cin>>temp;
int nowr=(temp-1)/m,nowc=(temp-1)%m;
if(flag[nowr][nowc]){
cout<<"Yes"<<endl;
return 0;
}
}
//这里也不要忘记初始位置
if(flag[n-1][m-1]){
cout<<"Yes"<<endl;
return 0;
}
cout<<"No"<<endl;
}
L题
Richadal和他的采矿车 (tzcoder8401)
思路
模拟
多关键字结构体排序
有一个坑点是时间可能会爆int,当目标矿总量
1
0
9
10^9
109,而每次只能运
1
1
1矿石时,所需搬运次数是
1
0
9
10^9
109,再乘以每次来回所需时间会超过int。
要学会分析数据范围,不要简单直接#define int long long
long long的计算常数大于int ,并且带来更多内存消耗,以及容易没有分析数据范围的习惯,爆long long的情况可能会反应不过来。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+7;
int n;
int x[maxn],y[maxn],z[maxn],s[maxn];
struct Node{
long long time;//完成采矿需要的时间
int tag;//编号
int cnt;//需要进行的往返次数
}node[maxn];
bool cmp1(Node a,Node b){//采矿快的在前
if(a.time==b.time) return a.tag<b.tag;
return a.time<b.time;
}
bool cmp2(Node a,Node b){//轮数少的在前
if(a.cnt==b.cnt) return a.tag<b.tag;
return a.cnt<b.cnt;
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n;
for(int i=0;i<n/2;i++) cin>>x[i]>>y[i]>>z[i]>>s[i];
for(int i=n/2;i<n;i++) cin>>x[i]>>y[i]>>s[i];
int target,k;
cin>>target>>k;
for(int i=0;i<n/2;i++){
node[i].tag=i+1;
node[i].cnt=(target-1)/s[i]+1;
node[i].time=node[i].cnt*(long long)(x[i]+y[i]+z[i]);
}
for(int i=n/2;i<n;i++){
node[i].tag=i+1;
node[i].cnt=(target-1)/s[i]+1;
node[i].time=node[i].cnt*(long long)(x[i]+y[i]);
}
int tempcnt=node[k-1].cnt;//排序后编号k的矿车位置可能改变
//提前记录下第二个输出
sort(node,node+n,cmp1);
cout<<node[k-1].tag<<' '<<node[k-1].cnt<<endl;
cout<<k<<' '<<tempcnt<<endl;
sort(node,node+n,cmp2);
cout<<node[k-1].tag<<' '<<node[k-1].cnt<<endl;
}
}
M题
yuyu与baby斗地主 (tzcoder8402)
思路
模拟
分类讨论
这里用一个change函数,把牌面转换用具体的数值来表示,数值越大牌面越大
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+7;
int n,m;
int cnt1[17],cnt2[17];//统计两个人每种牌有几张
int change(string s){
if(s.size()>1) return 10;
if(s[0]>='3'&&s[0]<='9') return s[0]-'0';
if(s[0]=='J') return 11;
if(s[0]=='Q') return 12;
if(s[0]=='K') return 13;
if(s[0]=='A') return 14;
if(s[0]=='2') return 15;
return 16;//剩下的只有大小王
}
int main(){
cin>>n>>m;
while(n--){
string s;
cin>>s;
cnt1[change(s)]++;
}
while(m--){
string s;
cin>>s;
cnt2[change(s)]++;
}
vector<int>S1,S2;//保存两个人的炸弹
if(cnt1[16]==2) S1.push_back(16);
for(int i=15;i>=3;i--)
if(cnt1[i]==4) S1.push_back(i);
if(cnt2[16]==2) S2.push_back(16);
for(int i=15;i>=3;i--)
if(cnt2[i]==4) S2.push_back(i);
if(S1.size()>S2.size()) cout<<"yuyu"<<endl;
else if(S1.size()<S2.size()) cout<<"baby"<<endl;
else{
if(S1.size()==0){//两个热都没有炸弹,平局
cout<<"draw"<<endl;
return 0;
}
int flag=0;//代表yuyu比拼炸弹赢baby的次数
for(int i=0;i<S1.size();i++){
flag+=S1[i]>S2[i]?1:-1;
}
if(flag>0) cout<<"yuyu"<<endl;
else if(flag<0) cout<<"baby"<<endl;
else{
if(S1[0]>S2[0]) cout<<"yuyu"<<endl;
else cout<<"baby"<<endl;
}
}
}