A. Choosing Ice Cream
题目链接:https://nanti.jisuanke.com/t/28201
题意:有n个ice cream,给你一个骰子k个面,问能否公平的(等概率选出一个ice cream)选择一个ice cream。如果能,求出投掷几次(或者需要几个骰子),不能则输出“unbounded”。
思路:
如果能够公平选择(即投掷后每个ice cream等概率出现),则需要满足 n | k ^(i).
那么,n | gcd(n,k).
意思就是,每次需要将n分为几个骰子,每个骰子出现的概率是相等的,(每个骰子上的ice cream也要均分k,因此是选取最大公约数)直到最后只需要一个骰子就能够选出来ice cream,此时,n为1.
这样就保证了每次选取概率都是相等的,相乘得到的最终概率也是等的。
例如
9 3
9个ice cream , 一个骰子3面,公约数3.
那么相当于分了3个骰子,每个骰子n/3 = 3 面,此时一个骰子够用。算法结束
8 2
8个ice 一个骰子2面,公约数2
分2个骰子,一个4面(一个骰子不够,继续分)。
变成了4个ice (对应n/=gcd(n,k)),一个骰子2面,公约数2
分2个骰子,一个2面,结束。
Code:
#include <bits/stdc++.h>
using namespace std;
int gcd( int a , int b ){
return b == 0 ? a : gcd( b , a % b );
}
int main(){
int T;
cin >> T;
int n , k ;
while( T-- ){
cin >> n >> k ;
int i;
for( i = 0 ; n > 1 && k > 1 ; i ++ ){
k = gcd( n , k );
n /= k;
}
if( n > 1 ){
printf("unbounded\n");
}else printf("%d\n",i);
}
return 0 ;
}
B. Failing Components
题目连接:https://nanti.jisuanke.com/t/28202
题意:给一个初始坏掉的零件,并给出依赖关系,所有依赖坏掉零件的零件也会在ts后坏掉,求坏掉的总个数以及时间。
思路:
一遍bfs,用优先队列按时间排序,每次取最先坏掉的,dis存每个零件到最初坏掉那个的距离(时间),要求这个的最短时间。
最后dis数组不为INF的则能够通过最初坏掉的传过来,即为坏掉的个数。
记录最大的距离即为总共需要的时间。
Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 1e4+66;
int n , m , s ;
struct Node{
int to ;
int w ;
friend bool operator< ( const Node &x, const Node &y){
return x.w > y.w;
}
};
std::vector<Node> v[AX];
int res ,cnt ;
int dis[AX];
void bfs(){
priority_queue<Node>que;
Node p;
p.to = s ;
p.w = 0 ;
dis[s] = 0 ;
que.push(p);
while( !que.empty() ){
Node tmp = que.top();
que.pop();
int t = tmp.to;
for( int i = 0 ; i < v[t].size() ; i++ ){
if( dis[v[t][i].to] > v[t][i].w + dis[t] ){
dis[v[t][i].to] = v[t][i].w + dis[t];
Node tt;
tt.to = v[t][i].to;
tt.w = dis[v[t][i].to];
que.push(tt);
}
}
}
}
int main(){
int T;
scanf("%d",&T);
int x ;
while( T-- ){
for( int i = 0 ; i <= n ; i++ ) v[i].clear();
memset( dis, INF ,sizeof(dis) ) ;
cnt = 0 ;
res = 0 ;
scanf("%d%d%d",&n,&m,&s);
while( m-- ){
Node tmp ;
scanf("%d%d%d",&tmp.to,&x,&tmp.w);
v[x].push_back(tmp);
}
bfs();
for ( int i = 1 ; i <= n ; i++ ){
if( dis[i] < INF ){
cnt ++;
res = max( res , dis[i] );
}
}
cout << cnt << ' ' << res << endl;
}
return 0 ;
}
D. Lift Problems
题目链接:https://nanti.jisuanke.com/t/28204
题意:电梯从0层往上走到n(不回头),1-n层每层都有要下电梯的人(可能为0),如果自己下面层的人下电梯,电梯停了愤怒就会增加1,如果跳过自己这层,往上走,则每走一层愤怒+1,直到电梯停止(愤怒停止增加),自己走回去(一直看错以为电梯拐回来。。。同学!没骨气啊,等电梯给你送回去呀!!)
思路:
dp,dp[i]表示到第i层停下的最小愤怒度。
用sum记录比自己去更高层的人数,初始电梯在0层时为总人数。
从1-n循环,每次将sum减少当前层人数,枚举i层前面所有层作为电梯前一个停的层数,找出停在i层时的最小愤怒度( j到i跳过的愤怒度+停下时要上更高层的所有人的愤怒度 )。
最后dp[n]即为所求。
Code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 2e3+66;
int a[AX];
int dp[AX];
int main(){
int T;
scanf("%d",&T);
int n ;
while( T-- ){
int sum = 0 ;
scanf("%d",&n);
for( int i = 1 ; i <= n ; i++ ){
scanf("%d",&a[i]);
sum += a[i];
}
dp[0] = 0 ;
for( int i = 1; i <= n ; i++ ){
sum -= a[i];
int skip = 0;
dp[i] = INF;
for( int j = i - 1 ; j >= 0 ; j-- ){
dp[i] = min( dp[i] , dp[j] + skip + sum );
skip += (i-j)*a[j];
}
}
printf("%d\n",dp[n]);
}
return 0 ;
}
E. Pawns
题目链接:https://nanti.jisuanke.com/t/28205
题意:每列有一个B,一个W,B在W上面,并且每次只能向对方的方向移动一个单位,但是如果B,W在边缘可以一次移动两个单位,W先手移动,问最后谁赢。
思路:待更
Code:
#include <bits/stdc++.h>
using namespace std;
const int AX = 20+6;
char G[AX][AX];
int main(){
int T;
scanf("%d",&T);
int n , m ;
while( T-- ){
scanf("%d%d",&n,&m);
for( int i = 0 ; i < n ; i++ ){
scanf("%s",G[i]);
}
int i = 0 ;
int w , b;
int move = 0 ;
int dou = 0 , white = 0 , black = 0 ;
for( int j = 0 ; j < m ; j++ ){
for( int k = 0 ; k < n ; k++ ){
if( G[k][j] == 'B' ) b = k ;
if( G[k][j] == 'W' ) w = k ;
}
if( b == 0 && w == n - 1 && n > 3 ) dou++;
else{
if( w == n - 1 && b < n - 3 ) white += n - 3 - b ;
else if( b == 0 && w > 2 ) black += w - 2 ;
move += w - b - 1;
}
}
if( n == 4 && dou % 2 || white > black || ( white >= black - 1 && move % 2 ) ){
printf("White wins\n");
}else printf("Black wins\n");
}
return 0 ;
}
F. Runway Planning
题目链接:https://nanti.jisuanke.com/t/28206
签到题不多说
Code:
#include <bits/stdc++.h>
using namespace std;
int main(){
int T;
cin >> T;
int n ;
while( T-- ){
cin >> n;
int t = n / 10 + ( n % 10 >= 5 ) ;
if( t > 18 ){
t %= 18;
}
if( t == 0 ) t = 18;
if( t < 10 ){
cout << 0 << t << endl;
}else{
cout << t << endl;
}
}
return 0 ;
}
H.Talent Selection
题目链接:https://nanti.jisuanke.com/t/28208
题意:给出n个人,其中f个喜欢的人,n-f个不喜欢的人,并给出他们的初始分数,现在要s个人晋级,你有k个加分项必须全部分配,每个人一项,问如何才能让尽量多的喜欢的人晋级。
思路:
二分答案key:
晋级s人,肯定要把高分先加给f个喜欢的人(分高的加的低),然后加给n-f的加。
假设已经给key个喜欢的人,s-key个不喜欢的人加分,剩下的分要加给没有晋级的人,所以要统计这个最大值,如果这个最大值小于等于我们选中的key个喜欢的人加分后的值,则这个key可能为结果。
Code:
#include <bits/stdc++.h>
using namespace std;
const int AX = 1e5+66;
int a[AX];
int c[AX];
int main(){
int T;
scanf("%d",&T);
while( T-- ){
int n , s , f;
scanf("%d%d%d",&n,&s,&f);
for( int i = 0 ; i < n ; i++ ){
scanf("%d",&a[i]);
}
int k ;
scanf("%d",&k);
for( int i = 0 ; i < k ; i++ ){
scanf("%d",&c[i]);
}
sort( a , a + f );
sort( a + f , a + n );
int l = max( 0 , s - ( n - f ) ); //答案的边界
int r = min( s , f ) + 1 ;
while( l < r ){//二分答案
int key = ( l + r ) >> 1 ;
int val = a[n-1-(s-key)]; // 初始化为不喜欢的人中最大的未加分的分值
//求除了key个喜欢的人和s-key个不喜欢的人,剩下的未加分的加上分数后的最大值
for ( int i = 0; i < k - f - ( s - key ) ; i ++ ){
val = max( val, a[ k - 1 - ( s - key ) - i ] + c[i] );
}
//如果val大于已经选择晋级的喜欢的人,说明key不是想要的结果
int j ;
for( j = 0 ; j < key && a[f-1-j] + (( k - key + j ) < 0 ? 0 : c[k-key+j] ) >= val ; j++ );
if( j == key ){
l = key + 1 ;
}else r = key;
}
printf("%d\n",l-1);
}
return 0 ;
}
J. Word Search
题目链接:https://nanti.jisuanke.com/t/28210
题意:给出一个网格,里面都是大写字母,又给出一堆单词,如果每一个单词都能在网格里面找到(横着,竖着找,斜着找这8个方向都可以),且每个单词出现一次,标记单词所在的字母,并输出剩下没有标记的字母。如果有一个单词出现2次以上,输出ambiguous,如果有单词没有找到,输出no solution。
如果全部标记没有结果输出,“empty solution”
思路:
数据小,暴力枚举。
每输入一个单词,就在网格找首单词出现的位置(这里也可以用pair存一下每个字母出现的位置,减少枚举量),然后搜8个方向的单词。
这里需要注意的就是回文的情况,如果是回文,那么这个单词就会出现两次。
还有单词长度为1的情况,那就是8次。
Code:
#include <bits/stdc++.h>
using namespace std;
const int AX = 50+6;
char G[AX][AX];
int mark[AX][AX];
char s[300];
int dir[8][2] ={
{1,0},
{0,1},
{0,-1},
{-1,0},
{1,1},
{-1,-1},
{1,-1},
{-1,1}
};
int main(){
int T;
scanf("%d",&T);
int q , n , m ;
while( T-- ){
scanf("%d%d%d",&q,&n,&m);
for( int i = 0 ; i < n ; i++ ){
scanf("%s",G[i]);
}
int f = 1;
int amb = 0;
bool palindrome ;
memset( mark , 0 , sizeof(mark) );
while( q-- ){
scanf("%s",s);
int len = strlen(s);
int kk ;
for ( kk = 0; kk < (len-1)/2 && s[kk] == s[len-1-kk]; kk++);
palindrome = (kk == (len-1)/2);
int cnt = 0 ;
for( int x = 0 ; x < n ; x++ ){
for( int y = 0 ; y < m ; y ++ ){
int k ;
for( int d = 0 ; d < 8 ; d++ ){
for( k = 0 ; k < len ; k++ ){
int xx = x + k * dir[d][0];
int yy = y + k * dir[d][1];
if( xx < 0 || xx >= n || yy < 0 || yy >= m || G[xx][yy] != s[k] ){
break;
}
}
if( k == len ){
cnt ++;
for( int j = 0 ; j < len ; j++ ){
int xx = x + j * dir[d][0];
int yy = y + j * dir[d][1];
mark[xx][yy] = 1 ;
}
}
}
}
}
if( !cnt ){
f = 0;
}
if (!( cnt == 1 || (palindrome && cnt == 2) || (len == 1 && cnt == 8)))
amb = 1;
}
if( !f ){
printf("no solution\n");
}
else if( amb ){
printf("ambiguous\n");
}else{
int falg = 0 ;
for( int i = 0 ; i < n ; i++ ){
for( int j = 0 ; j < m ; j++ ){
if( !mark[i][j] ){
falg = 1;
printf("%c",G[i][j]);
}
}
}
if(falg) printf("\n");
else{
printf("empty solution\n");
}
}
}
return 0 ;
}

本文精选了几道算法竞赛题目并提供了详细的解题思路与代码实现,涵盖了概率论、图论、动态规划等多个方面。
2252

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



