比赛链接—> 2019河北省大学生程序设计竞赛
A Battle of Balls(并查集)
题意: 有一个100×100的平面,给出圆的半径和n个点的坐标,问该圆能否从底部y=0到达顶部y=100。
题解: 计算每两点之间的距离,如果距离大于圆的半径时将两点连起来(用并查集并起来),若最后将顶部和底部分隔开(同一个集合)则说明不能到达,在这里要注意输入x的时候要判断x和边界x=0 x=100之间能否通过,如果不能通过则并在一起,还有这里坐标都是double判断的时候要注意精度的问题,判断a大于b不是简单的a > b,而是a - b > eps(eps=1e-8)。
#include <cstdio>
#include <cmath>
using namespace std ;
const int N = 1e3 + 50 ;
const double eps = 1e-8 ;
int s[N] ;
double x[N] , y[N] ;
int find(int x){
if(x!=s[x]) s[x]=find(s[x]) ;
return s[x] ;
}
double distance(int i,int j){
return sqrt((x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j])) ;
}
void link(int a ,int b){
a = find(a) , b = find(b) ;
if(a != b) s[a]=b ;
}
int main(){
int t ; scanf("%d",&t) ;
while(t--){
int n ; double r ;
scanf ("%d%lf",&n,&r) ;
for (int i=0;i<=n+1;++i) s[i]=i;
r *= 2 ;
for (int i=1 ; i<=n ; ++i){
scanf("%lf%lf",&x[i],&y[i]) ;
if(x[i]-r < eps) link(0,i) ; //边界是否能通过
if(100.00-x[i]-r < eps) link(i,n+1) ;
}
for(int i=1 ; i<=n ; ++i)
for (int j=i+1 ; j<=n ;++ j) //每两点间的距离小于圆的直径则不能过,将两点连起来
if(distance(i,j)-r < eps) link(i,j) ;
if (find(0) == find(n+1)) printf ("No\n") ;
else printf ("Yes\n") ;
}
return 0 ;
}
B-Icebound and Sequence(二分+快速幂取模)
题意:给出q,n,p要求输出
∑
i
=
1
n
\sum_{i=1}^{n}
∑i=1nqi mod p。
题解:这道题本来是可以用等比数列求和公式然后用快速幂取模,但是因为不取模long long会超,但是取模了去除于(q-1)又不准确。所以可以直接计算连加,但是如果直接计算n<=1e9会超时,所以用到二分的思想。
当n为奇数时:s(n) = s(
n
2
\frac{n}{2}
2n) + s(
n
2
\frac{n}{2}
2n)*fastpow(q,
n
2
\frac{n}{2}
2n,mod) + fastpow(q,n,mod) ;
当n为偶数时:s(n) = s(
n
2
\frac{n}{2}
2n) + s(
n
2
\frac{n}{2}
2n)*fastpow(q,
n
2
\frac{n}{2}
2n,mod) ;
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
using namespace std ;
typedef long long ll ;
ll n , q , p ;
ll fastpow(ll a , ll b , ll mod){
ll res = 1 ;
while(b){
if (b&1)
res = res*a%mod ;
a = a*a%mod ;
b >>= 1 ;
}
return res ;
}
ll fun(ll t){
if (t == 1) return q%p ;
if (t%2== 0) return (fun(t/2)+fun(t/2)*fastpow(q,t/2,p))%p ;
else return (fun(t/2)+fun(t/2)*fastpow(q,t/2,p)+fastpow(q,t,p))%p ;
}
int main(){
int t ; cin >> t ;
while(t --){
cin >> q >> n >> p ;
cout << fun(n) << endl ;
}
return 0;
}
C 分治(区间dp / 记忆化搜索)
题意: 国王要攻打n个连续的城市,每个城市有赔偿金ai,攻打第i个城市要赔偿给旁边两个城市各ai赔偿金,当攻打一个国家 i 时,为了防止国家间的联合对抗,需要给该国家周围,所有未被攻占的国家支付ai 个金币,即对于国家 i,它左侧第一个已被攻打的国家为 l,右侧第一个已被攻打的国家为 r,则他需要给[l+1,i-1] 和 [i+1,r-1] 之间的国家支付金币。如果 l 不存在,则需要给 [1, i-1] 之间的所有国家支付金币;若 r 不存在,则需要给 [i+1,n] 之间的所有国家支付金币。
题解:大佬题解
区间dp解法:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std ;
const int N = 105 ;
typedef long long ll ;
ll a[N] , dp[N][N] ;
ll INF = 0x3f3f3f3f ;
int main(){
int t ; scanf ("%d",&t) ;
while(t --){
int n ; scanf ("%d",&n);
memset(dp,INF,sizeof(dp)) ;
for (int i=1 ; i <= n ;++i) dp[i][i]=0;
for (int i=1 ; i<=n ; ++i) scanf("%d",&a[i]) ;
for (int len=1 ; len<n ; ++ len){
for (int i=1 ; i<=n-len ; ++i){
int j = i+len ;
for (int k=i ; k <= j ; ++ k)
dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+a[k]*len) ;
dp[i][j]=min(dp[i][j],dp[i+1][j]+a[i]*len) ;//边界情况
dp[i][j]=min(dp[i][j],dp[i][j-1]+a[j]*len) ;
}
}
printf ("%lld\n",dp[1][n]) ;
}
return 0 ;
}
记忆化搜索解法:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std ;
typedef long long ll ;
const int N = 105 ;
ll a[N] , dp[N][N] ;
ll dfs(int l , int r){
if (dp[l][r] != -1) return dp[l][r] ; //记忆化搜索
if (r <= l) return 0 ;
ll ans = ((ll)1<<63)-1 ;
for (int i=l ; i<=r ; ++i) //枚举攻打点
ans = min(ans,dfs(l,i-1)+dfs(i+1,r)+a[i]*(r-l)) ;
return dp[l][r]=ans ;
}
int main(){
int t ; scanf("%d",&t);
while(t--){
int n ; scanf("%d",&n) ;
for (int i=1 ; i<=n ; ++i) scanf("%d",&a[i]);
memset(dp,-1,sizeof(dp)) ;
printf ("%lld\n",dfs(1,n)) ;
}
return 0;
}
F Take Apples(博弈)
题解
emmmmmm说实话我还是不懂博弈,题解看了半天还是不懂emmmmm。。。。。。。题是偷偷参考大佬的
#include <cstdio>
int main(){
int m , n , s ;
while(~scanf("%d%d%d",&m,&n,&s)){
if(n <= s && m%(s+1) == 0)
printf ("Bob\n") ;
else printf ("Alice\n") ;
}
return 0 ;
}
J 舔狗(拓扑排序)
题意: 你需要给这 n 只舔狗配对,对于舔狗 i,他可以和他朝思暮想的人 ai配对。另外,喜欢 i 的其他舔狗也可以和他配对。你需要让没有被配对的舔狗尽量少。
题解: 一个用到优先队列的拓扑排序,一般的拓扑排序是要求没有环的因为要从入度为0开始进队出队,但是这道题是从入度最小的出队,先将没有人喜欢的配对然后用vis数组标记已经配对。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std ;
const int N = 1e6+5 ;
struct node{
int id , in , out ;
bool operator < (const node & a)const {//运算符重载
return in > a.in ; //入度小的优先
}
}v[N] ;
int in[N] ;
priority_queue<node> q ;
bool vis[N] ;
int main(){
int n ; scanf("%d",&n);
for (int i=1;i<=n;++i){
int x ; scanf("%d",&x) ;
v[i].id=i , v[i].out=x ;
++in[x] ;
}
//将全部点都进队
for (int i=1 ; i<=n ; ++i) v[i].in=in[i] , q.push(v[i]) ;
int cnt = 0 ;
while(!q.empty()){
node t = q.top() ; q.pop() ;
if (vis[t.id]) continue ; //已配对的跳过
if (!vis[t.out]){
vis[t.out] = true ; vis[t.id]=true ; ++ cnt ; //配对成功
int next = v[t.out].out ;
//修改指向的入度再重新入队,因为是优先队列而且有标记是否配对所以多入队几次也没关系
in[next] -- , v[next].in = in[next] ;
q.push(v[next]) ;
}
}
printf ("%d\n",n-cnt*2) ;
return 0 ;
}
L Smart Robot(暴力深搜)
题意: n × n 的矩阵中每个格子的数为0 ~ 9 , 机器人能走到的地方为魔法数字,例如走了三个先后为0 7 8 那么该魔法数字为78,问机器人不能搜索到的最小数为多少。
题解: 直接暴力搜索,不会超时。。。。。。。。然后能搜索到的标记下,最后顺序找一下哪个没有标记然后输出。
#include <cstdio>
const int N = 55 ;
int a[N][N] , n ;
bool vis[100050] ;
int dir[4][2] = {{-1,0},{0,-1},{1,0},{0,1}} ;
void dfs(int x ,int y,int step,int val){ //坐标,位数 ,当前数的值
vis[val] = true ; //标记能够搜索到
if (step == 5) return ; //位数超过五就不用再搜索了
for (int i=0 ; i<4 ; ++i){
int dx = x + dir[i][0] ;
int dy = y + dir[i][1] ;
if (dx>=1&&dx<=n&&dy>=1&&dy<=n)
dfs(dx,dy,step+1,val*10+a[dx][dy]) ; //搜索下一个数
}
}
int main(){
scanf ("%d",&n) ;
for (int i=1 ; i<=n ; ++i)
for (int j=1 ; j<=n ; ++ j)
scanf ("%d",&a[i][j]) ;
for (int i=1 ; i<=n ; ++i)
for (int j=1 ; j<=n ; ++j)
dfs(i,j,1,a[i][j]) ;
int i = 0 ;
while(vis[i] && i < 100050) ++ i ;
printf ("%d\n",i) ;
return 0 ;
}
本文解析了2019年河北省大学生程序设计竞赛中的ABBattleofBalls、BIceboundandSequence、C分治、FTakeApples、J舔狗、LSmartRobot等题目,涵盖并查集、二分+快速幂取模、区间DP、记忆化搜索、博弈、拓扑排序及暴力深搜等多种算法。详细介绍了每道题目的题意、解题思路及代码实现。
3685

被折叠的 条评论
为什么被折叠?



