2025“钉耙编程”中国大学生算法设计春季联赛(8)
按照难度顺序
1007 架子鼓
只要算出来击打两种鼓的时间,取并集即可。用pii维护分数,每次要把分数变成最简分数
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;
void solve(){
int n,m;
cin>>n>>m;
int ans=1;
set<pair<int,int>> s;
int p=0,q=0;
for(int i=1;i<=n;i++){
int a,b;
cin>>a>>b;
if(p!=0 && q!=0){
s.insert({p,q});
}
if(p==0 && q==0){
p=a;
q=b;
}else{
int u=a*q+p*b;
int v=b*q;
p=u/gcd(u,v);
q=v/gcd(u,v);
}
}
// for(auto [a,b]:s){
// cout<<a<<" "<<b<<endl;
// }
p=0,q=0;
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
if(p!=0 && q!=0){
if(s.count({p,q})) ans++;
// cout<<p<<" "<<q<<endl;
}
if(p==0 && q==0){
p=a;
q=b;
}else{
int u=a*q+p*b;
int v=b*q;
p=u/gcd(u,v);
q=v/gcd(u,v);
}
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
return 0;
}
1003 独到寒山顶
更是签到
多源BFS,把所有可能的起点先放进队列里,开始bfs,最后取重点的最小值即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;
void solve(){
int n,m;
cin>>n>>m;
//n列m行
vector<vector<int>> g(m+10,vector<int>(n+10,1)),dist(m+10,vector<int>(n+10,inf));
for(int i=1;i<=n;i++){
int r;
cin>>r;
while(r--){
int idx;
cin>>idx;
g[idx][i]=0;
}
}
queue<pii> q;
for(int i=1;i<=n;i++){
if(g[1][i]==1){
q.push({1,i});
dist[1][i]=1;
}
}
while(q.size()){
auto [x,y]=q.front();
q.pop();
int dx[]={-1,0,1,0};
int dy[]={0,1,0,-1};
for(int i=0;i<4;i++){
int a=x+dx[i];
int b=y+dy[i];
if(a<1 || a>m || b<1 || b>n) continue;
if(!g[a][b]) continue;
if(dist[a][b]>dist[x][y]+1){
dist[a][b]=dist[x][y]+1;
q.push({a,b});
}
}
}
int ans=inf;
for(int i=1;i<=n;i++){
ans=min(ans,dist[m][i]);
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
return 0;
}
1001 拼图游戏
学习bitset的典题
从前往后,对每一行,二分的去找在哪一列是满足条件的,期间用bitset维护每一列的颜色,加一点前缀和的思想, 在纸上画一下很快就明白这个流程了
注意bitset的大小在编译期确定,所以需要开常量大小
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;
void solve(){
int n,m,k;
cin>>n>>m>>k;
int g[n+10][m+10];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>g[i][j];
}
}
//对每一列开一个bitset,记录颜色
vector<bitset<2001>> bits(m+10);
//把涉及不到的都改成1
for(int i=1;i<=m;i++){
bits[i].set(0,1);
for(int j=k+1;j<=2000;j++){
bits[i].set(j,1);
}
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
bits[j][g[i][j]]=1;
bits[j]|=bits[j-1];
}
int l=1,r=m;
while(l<r){
int mid=l+r>>1;
if(bits[mid].all()) r=mid;
else l=mid+1;
}
if(bits[l].all())ans+=(m-l+1);
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
return 0;
}
1002 缓存系统
分组背包模板题。
因为对一组内,取第j个物品需要把这一组的前j个物品都取完,所以可以把组内第j个物品的看成,这一组内的前j个物品的总和。前缀和即可。就转换成了每组最多取一个的分组背包问题
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;
void solve(){
int n,m,x;//组数,每组内的个数,背包容量
cin>>n>>m>>x;
int sum=0;
vector<vector<int>> w(n+10,vector<int>(m+10));// 读取次数 权值
vector<vector<int>> a(n+10,vector<int>(m+10));// 内存 代价
vector<vector<int>> f(n+10,vector<int>(x+10));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j]>>w[i][j];
sum+=w[i][j];
}
for(int j=2;j<=m;j++){
w[i][j]+=w[i][j-1];
a[i][j]+=a[i][j-1];
}
}
//分组背包
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k<=x;k++){
// 不选i组第j个物品
f[i][k]=max(f[i][k],f[i-1][k]);
}
for(int k=x;k>=a[i][j];k--){
//选i组第j个物品
f[i][k]=max(f[i-1][k-a[i][j]]+w[i][j],f[i][k]);
}
}
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=x;j++){
ans=max(ans,f[i][j]);
}
}
cout<<sum-ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
return 0;
}
1005 咒语附魔
数据纯随机,直接暴力就行。因为01随机生成,所以在每个数的位置都有50%的可能跳出,所以复杂度一定不会很大。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;
void solve(){
int n,m;
cin>>n>>m;
string s,t;
cin>>s>>t;
s=" "+s;
t=" "+t;
vector<vector<int>> pos(n+10);
for(int i=1;i<=m-n+1;i++){
int cnt=0;
for(int j=i,k=1;j<=m && k<=n;j++,k++){
if(t[j]!=s[k]) cnt++;
else break;
}
pos[cnt].push_back(i);
}
vector<string> strs;
for(int i=n+9;i>=0;i--){
if(pos[i].size()){
for(auto idx:pos[i]){
string tmp;
for(int i=1;i<=n;i++){
if(s[i]==t[i+idx-1]) tmp.push_back('0');
else tmp.push_back('1');
}
strs.push_back(tmp);
}
break;
}
}
sort(strs.begin(),strs.end());
if(strs.size()==0){
cout<<'0'<<endl;
}else{
int ans=0;
string str=strs.back();
for(auto ch:str){
if(ch=='1') ans++;
}
cout<<ans<<endl;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
return 0;
}
1010 字符串哈希
好神奇的题目,每次做杭电都能对已有的知识点有新理解
因为B(s)的值范围很小,随意枚举这个值 i ,0-10006。
用i算出 i * i * i * c + i * i * d + i * e + f
算出这个值后,又因为A(s) 的哈希值不会重复,所以可以用这个值,去反推出来字符串。这个过程只需要把这个值当成一个27进制的数字即可。推出字符串,如果符合条件,则ans++。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
using lll=__int128_t;
const ll inf=1e18;
const int mod =1e9+7;
const int M=10007;
void solve(){
int k,c,d,e,f;
cin>>k>>c>>d>>e>>f;
vector<int> p10(12);
p10[0]=1;
for(int i=1;i<=10;i++){
p10[i]=p10[i-1]*10%M;
}
int ans=0;
for(int i=0;i<M;i++){
//现在是在枚举bi,要根据bi的算出来哈希值,去反推A(s)的s,在验证A(s)是否等于B(s)
lll val=i*i*i*c+i*i*d+i*e+f;
vector<int> a;
while(val){
a.push_back((int)val%27);
val/=27;
}
if(a.size()>k || a.size()==0) continue;
int tmp=0;
bool f=0;
for(int j=0;j<a.size();j++){
if(a[j]==0) f=1;
tmp+=a[j]*p10[j];
tmp%=M;
}
if(!f && tmp==i) ans++;
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
return 0;
}

浙公网安备 33010602011771号