2022“杭电杯”中国大学生算法设计超级联赛(1)

比赛链接

2022“杭电杯”中国大学生算法设计超级联赛(1)

7139. Dragon slayer

给定一张 n×m 的地图,有 k 道墙,每次操作可以消除一道墙,求由起点到终点的最小操作数

解题思路

bfs,二进制优化

关键在于图的位置表示,即点的坐标是小数,而墙的坐标是整数,将墙的横竖分别用两个数组表示,点左右/上下移动时用竖/横墙判断,二进制表示所有的墙是否操作,bfs 判断起点是否能到终点,注意点是否能经过墙的一些细节

  • 时间复杂度:O(2k×nm)

代码

// %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=20; int res,t,n,m,k,f[1<<15],dx[]={-1,0,1,0},dy[]={0,1,0,-1}; bool v[N][N],lr[N][N],ud[N][N]; struct A { int x[2],y[2]; }a[N]; PII s,e; bool bfs(int st) { for(int i=0;i<=n;i++) for(int j=0;j<=m;j++)v[i][j]=lr[i][j]=ud[i][j]=0; for(int i=0;i<k;i++) if((st>>i&1)==0) { if(a[i].x[0]==a[i].x[1]) for(int j=a[i].y[0];j<a[i].y[1];j++)ud[a[i].x[0]][j]=1; else for(int j=a[i].x[0];j<a[i].x[1];j++)lr[j][a[i].y[0]]=1; } queue<PII> q; q.push({s.fi,s.se}); v[s.fi][s.se]=1; while(q.size()) { auto t=q.front(); if(t==e)return true; q.pop(); int x=t.fi,y=t.se; if(x+1<n&&!v[x+1][y]&&!ud[x+1][y]) { v[x+1][y]=1; q.push({x+1,y}); } if(x-1>=0&&!v[x-1][y]&&!ud[x][y]) { v[x-1][y]=1; q.push({x-1,y}); } if(y+1<m&&!v[x][y+1]&&!lr[x][y+1]) { v[x][y+1]=1; q.push({x,y+1}); } if(y-1>=0&&!v[x][y-1]&&!lr[x][y]) { v[x][y-1]=1; q.push({x,y-1}); } } return false; } int main() { help; for(cin>>t;t;t--) { cin>>n>>m>>k; cin>>s.fi>>s.se>>e.fi>>e.se; for(int i=0;i<k;i++) { cin>>a[i].x[0]>>a[i].y[0]>>a[i].x[1]>>a[i].y[1]; if(a[i].x[0]==a[i].x[1]) { if(a[i].y[0]>a[i].y[1])swap(a[i].y[0],a[i].y[1]); } else if(a[i].x[0]>a[i].x[1])swap(a[i].x[0],a[i].x[1]); } for(int i=0;i<(1<<k);i++)f[i]=0; res=k; for(int i=0;i<(1<<k);i++) { if(f[i])continue; if(bfs(i)) { res=min(res,__builtin_popcount(i)); for(int j=0;j<k;j++)f[i|1<<j]=1; } } cout<<res<<'\n'; } return 0; }

7140. Backpack

给出 n 个物品,容量为 m,体积和价值分别为 vi,wi ,求恰好将背包装满的价值最大异或和

解题思路

dp,bitset优化dp

  • 状态表示:f[i][j][k] 表示前 i 个物品是否存在异或和为 j,体积为 k 的情况

  • 状态计算:f[i][j][k]=f[i1][j][k]|f[i1][jw][kv]

显然可以滚动掉第一维,即每次求解 f[j][k]|f[i1][jw][kv],可由 bitset 优化,而 bitset 可以一维一维处理,所以另外开一个 bitset 数组,表示上一个数组,同时先表示出体积,即 k=kv,在 bitset 上对应左移 v

  • 时间复杂度:O(n3/64)

代码

// %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=1024; bitset<N> f[N],g[N]; int n,m,t; int main() { help; for(cin>>t;t;t--) { cin>>n>>m; for(int i=0;i<N;i++)f[i].reset(); f[0][0]=1; for(int i=1;i<=n;i++) { int v,w; cin>>v>>w; for(int j=0;j<N;j++)g[j]=f[j],g[j]<<=v; for(int j=0;j<N;j++)f[j]|=g[j^w]; } int res=-1; for(int i=0;i<N;i++) if(f[i][m])res=i; cout<<res<<'\n'; } return 0; }

7141. Ball

给定 n 个点,问有多少种方案,使得选出 3 个点中边的长度的中位数为素数

解题思路

思维,bitset优化

先找出为素数的边,对于这样的边,只需找出有多少点到该边两端点的距离一大一小,可以先将所有边排序,这样找的小边一定在前面,用 bitset 优化,设素数边两端点为 x,yc[x][z] 表示点 x 和 点 z 是否遍历过,即是否 |xz| 是否是小边,同理有 c[y][z],如果 c[x][z]c[y][z] 一个为 0 一个为 1,即 c[x][z]c[y][z]=1,即一大一小的边,则 z 满足条件,所有 x,y 对答案的贡献为 c[x]c[y]1 的数量

  • 时间复杂度:O(n3/64)

代码

// %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=2005,M=2e5+5; int n,m,t; bool v[M]; PII a[N]; bitset<N> c[N]; struct A { int x,y,d; }b[N*N]; int main() { help; v[1]=true; for(int i=2;i<M;i++) { if(v[i])continue; for(int j=i+i;j<M;j+=i)v[j]=true; } for(cin>>t;t;t--) { cin>>n>>m; int cnt=0; for(int i=1;i<=n;i++) { cin>>a[i].fi>>a[i].se; for(int j=1;j<i;j++)b[++cnt]={i,j,abs(a[i].fi-a[j].fi)+abs(a[i].se-a[j].se)}; } sort(b+1,b+1+cnt,[](A &a,A &b){return a.d<b.d;}); for(int i=0;i<N;i++)c[i].reset(); LL res=0; for(int i=1;i<=cnt;i++) { int x=b[i].x,y=b[i].y; if(!v[b[i].d]) res+=(c[x]^c[y]).count(); c[x][y]=c[y][x]=1; } cout<<res<<'\n'; } return 0; }

7146. Laser

棋盘中有 n 个点,问是否存在一个点,其按米字形朝 8 个方向射出的光线能否包含所有点

解题思路

思维,计算几何

假设存在这样的点,假设第一个点在 米字形竖线/横线/正对角线/反对角线,枚举其他点,一旦与第一个点不同在 米字形竖线/横线/正对角线/反对角线 上,则可以确定米字形中心,进而判断是否满足要求

  • 时间复杂度:O(12×n)

代码

// %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=5e5+5; int t,n; PII point[N],back[N]; bool pd(int x,int y) { return x==0||y==0||x==y||x==-y; } bool ck(int x,int y) { for(int i=1;i<=n;i++) if(!pd(x-point[i].fi,y-point[i].se))return false; return true; } bool ck() { bool flag=true; for(int i=2;i<=n;i++) { if(point[i].fi==point[1].fi)continue; flag=false; if(ck(point[1].fi,point[i].se))return true; if(ck(point[1].fi,point[i].se-(point[i].fi-point[1].fi)))return true; if(ck(point[1].fi,point[i].se+(point[i].fi-point[1].fi)))return true; } return flag; } int main() { for(scanf("%d",&t);t;t--) { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d%d",&back[i].fi,&back[i].se); bool f=false; //竖线 for(int i=1;i<=n;i++)point[i]=back[i]; if(ck())f=true; //横线 for(int i=1;i<=n;i++)point[i]={back[i].se,back[i].fi}; if(ck())f=true; //正对角线 for(int i=1;i<=n;i++)point[i]={back[i].fi-back[i].se,back[i].fi+back[i].se}; if(ck())f=true; //反对角线 for(int i=1;i<=n;i++)point[i]={back[i].fi+back[i].se,back[i].fi-back[i].se}; if(ck())f=true; puts(f?"YES":"NO"); } return 0; }

7149. Alice and Bob

aii(0in),Alice 每次可以将这些数划分为两个集合,Bob 每次可以删除一个集合,同时另外一个集合所有数减一。如果任何时候存在 0Alice 胜,否则 Bob

解题思路

博弈论

需要对 Alice 构造一种必胜策略,考虑将 aii 平分,即每次平分进两个集合,无论 Bob 如何选择,而由于每次平分后另外一个集合所有数减一,相当于将 i 减一,逆序处理所有的 ai,累计所有贡献,只要判断最后轮次的时候是否还存在数即可,因为划分集合后选择权在 Bob 手上,如果 Alice 不采取平分的策略,Bob 一定会删除对自己最有利的集合,而如果采取平分策略,即主动权掌握在 Alice 手上,无论 Bob 如何操作,其结果都是一定的。另一种理解:平分策略的任一子集是非平分策略的两个子集的中和,如果采取非平分策略,Bob 可以删除使对 Alice 平分策略更差的子集,这样可能平分策略能获胜但有一步采取非平分策略结果却败了,故平分策略是最优策略

  • 时间复杂度:O(n)

代码

// %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=1e6+5; int a[N],n,t; int main() { help; for(read(t);t;t--) { cin>>n; for(int i=0;i<=n;i++)cin>>a[i]; for(int i=n;i>=1;i--)a[i-1]+=a[i]/2; puts(a[0]?"Alice":"Bob"); } return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16496292.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(339)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2021-07-19 牛客2021暑假多校训练营2
点击右上角即可分享
微信分享提示