牛客小白月赛32
A. 拼三角
签到题,暴力DFS选C(6,3)即可,由于三个边有大小关系,前期排个序,可以利于后期三角形判别,选的三个数必定由小到大,a < b < c,只需判a+b > c即可。
代码
#include <bits/stdc++.h>
#define ll long long
#define mem( f, x ) memset( f, x, sizeof(f))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
#define pk push_back
using namespace std;
const int M = 40;
const int N = 50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
int m, n;
int a[6];
bool ans = 0;
int nums1[3], nums2[3];
bool vis[6];
bool judge( int p1, int p2, int p3 ){
return p1+p2 > p3;
}
void dfs( int pos, int cnt ){
if( ans ) return;
if(cnt == 3 ){
if( judge( nums1[0], nums1[1], nums1[2] ) ){
int ct = 0;
for( int i = 0; i < 6; i++ ){
if( !vis[i] )
nums2[ct++] = a[i];
}
if( judge( nums2[0], nums2[1], nums2[2] ) )
ans = 1;
}
return;
}
for( int i = pos+1; i < 6; i++ ){
if( !vis[i] ){
vis[i] = 1;
nums1[cnt] = a[i];
dfs( i, cnt+1 );
vis[i] = 0;
}
}
}
int main( ){
int t;
scanf( "%d", &t );
while( t-- ){
ans = 0;
for( int i = 0; i < 6; i++ )
scanf( "%d", a+i );
sort( a, a+6 );
dfs( -1, 0 );
if( ans ) printf( "Yes\n" );
else printf( "No\n" );
}
return 0;
}
B. 相对分子质量
比赛的时候题目没看清题目,没看到有括号,浪费了一点时间,很常规的运算式处理。用栈维护括号的计算顺序即可。唯一需要考虑的就是入栈的顺序问题。当一个元素完整出现的时候马上入栈;但后面出现顺序的时候,栈顶元素弹出来,乘系数之后再入栈;当出现左括号的时候入栈-1做标记,当出现有括号的时候,不断弹出栈直到遇到左括号-1为止,计算括号内部运算式的和之后将和直接入栈。
代码
#include <bits/stdc++.h>
#define ll long long
#define mem( f, x ) memset( f, x, sizeof(f))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
#define pk push_back
using namespace std;
const int M = 40;
const int N = 50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
int m, n;
unordered_map<string,int> mp;
ll solve( string &s ){
int len = s.length();
string tmp = "";
stack<int> sk;
int i = 0;
while( i < len ){
if(s[i] >= 'A' && s[i] <= 'Z'){
tmp = s[i++];
while( s[i] >= 'a' && s[i] <= 'z' && i < len )
tmp += s[i++];
sk.push(mp[tmp]);
}
else{
if( isdigit(s[i]) ){
int num = 0;
while( isdigit(s[i]) && i < len )
num = num*10+(s[i++]-'0');
int tp = sk.top();
sk.pop();
sk.push(tp*num);
}
else if( s[i] == '('){
sk.push(-1);
i++;
}
else if( s[i] == ')'){
ll cur = sk.top();
sk.pop();
ll tp = 0;
while(cur != -1){
tp += cur;
cur = sk.top();
sk.pop();
}
sk.push(tp);
i++;
}
}
}
ll ans = 0;
while( !sk.empty( ) ){
ans += sk.top();
sk.pop();
}
return ans;
}
int main( ){
string s;
int x;
while( scanf( "%d %d", &m, &n ) != EOF ){
mp.clear();
while( m-- ){
cin >> s >> x;
mp[s] = x;
}
while( n-- ){
cin >> s;
cout << solve(s) << endl;
}
}
return 0;
}
C. 消减整数
很容易想到的贪心,先不断的乘2之后消减,余下的不能减的可以在中间步骤不乘2做减法,因此可以暴力的正着不重复的减一次,倒着可重复的补一次中间的不乘2做减法,这一步可以重复做,因此可重复减。
代码
#include <bits/stdc++.h>
#define ll long long
#define mem( f, x ) memset( f, x, sizeof(f))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
#define pk push_back
using namespace std;
const int M = 40;
const int N = 30;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
ll m, n;
ll pw2[N];
void init( ){
pw2[0] = 1;
for( int i = 1; i < N; i++ )
pw2[i] = pw2[i-1]<<1ll;
}
int main( ){
int t;
init();
scanf( "%d", &t );
while( t-- ){
scanf( "%lld", &n );
int cnt = 0;
int i;
for( i = 0; i < N; i++ ){
if( n-pw2[i] >= 0 ){
n -= pw2[i];
cnt++;
}
else
break;
}
for( ; i >= 0; i-- ){
while( n-pw2[i] >= 0 ){
n -= pw2[i];
cnt++;
}
}
printf( "%d\n", cnt );
}
return 0;
}
E.春游
贪心,哪个船人均消费少先优先安排人到哪个船。有剩下的人,懒得处理细节的可以做上下扰动处理,只有三人船和二人船,1. 若优先安排三人船,那可能会剩下零个人、一个人或者两个人,可以在原来的三人船中拆0或1或2条船人到剩下的人中重新组合成一批人,并把这批人分到二人船中;2. 若优先安排二人船,那可能会剩下零个人或一个人,可以在原来的二人船中拆0或1或2或3条到剩下的人当中重新组合成一批人,并把这批人分到三人船中。这些情况取最小值即可。
代码
#include <bits/stdc++.h>
#define ll long long
#define mem( f, x ) memset( f, x, sizeof(f))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
#define pk push_back
using namespace std;
const int M = 40;
const int N = 30;
const int MOD = 1e9+7;
const ll INF = 1e18+50;
ll a, b, n;
int main( ){
int t;
scanf( "%d", &t );
while( t-- ){
scanf( "%lld %lld %lld", &n, &a, &b );
ll ans = INF;
if( a*3 <= b*2 ){
ll num_2 = (n+1)/2;
for( ll i = 0; i <= 3; i++ ){
ll cur_2 = num_2-i;
ll cur_3 = (n-cur_2*2+2)/3;
ans = min( ans, a*cur_2+b*cur_3);
}
}
else{
ll num_3 = (n+2)/3;
for( ll i = 0; i <= 2; i++ ){
ll cur_3 = num_3-i;
ll cur_2 = (n-cur_3*3+1)/2;
ans = min(ans, a*cur_2+b*cur_3);
}
}
printf( "%lld\n", ans );
}
return 0;
}
F. 五连珠
数位dp的思想,5*5的矩阵,5行每行5个数可以用5个二进制数表示,同理5列、主对角线、副对角线都可以分别用5个二进制表示。共12个数,每个位置的数可以用map映射,某行被消除时,对应的数会变成2^5-1=31,然后消除过程中判一下即可。
代码
#include <bits/stdc++.h>
#define ll long long
#define mem( f, x ) memset( f, x, sizeof(f))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
#define pk push_back
using namespace std;
const int M = 40;
const int N = 30;
const int MOD = 1e9+7;
const ll INF = 1e18+50;
int m, n;
int main( ){
int t;
int at[12], bt[12];
scanf( "%d", &t );
while( t-- ){
mem(at, 0);
mem(bt, 0);
map<int,pii> mpa;
map<int,pii> mpb;
int x;
for( int i = 0; i < 5; i++ ){
for( int j = 0; j < 5; j++ ){
scanf( "%d", &x );
mpa[x] = mk(i,j);
}
}
for( int i = 0; i < 5; i++ ){
for( int j = 0; j < 5; j++ ){
scanf( "%d", &x );
mpb[x] = mk(i,j);
}
}
int up = 31;
bool flaga = 0, flagb = 0;
int ans = 0;
for( int i = 0; i < 25; i++ ){
scanf( "%d", &x );
pii p = mpa[x];
at[p.fi] += 1<<p.se;
if( at[p.fi] == up )
flaga = 1;
at[5+p.se] += 1<<p.fi;
if( at[5+p.se] == up )
flaga = 1;
if( p.fi == p.se ){
at[10] += 1<<p.fi;
if( at[10] == up )
flaga = 1;
}
if( p.fi+p.se == 4 ){
at[11] += 1<<p.fi;
if( at[11] == up )
flaga = 1;
}
p = mpb[x];
bt[p.fi] += 1<<p.se;
if( bt[p.fi] == up )
flagb = 1;
bt[5+p.se] += 1<<p.fi;
if( bt[5+p.se] == up )
flagb = 1;
if( p.fi == p.se ){
bt[10] += 1<<p.fi;
if( bt[10] == up )
flagb = 1;
}
if( p.fi+p.se == 4 ){
bt[11] += 1<<p.fi;
if( bt[11] == up )
flagb = 1;
}
if( !ans && (flaga || flagb)){
if( flaga && flagb )
ans = 0;
else if( flaga )
ans = 1;
else if( flagb )
ans = 2;
}
}
printf( "%d\n", ans );
}
return 0;
}
G. 有始有终
基本思路是BFS,但中间电梯的处理需要注意一下,在每个位置都能建电梯,建完电梯后需要DFS处理出建完电梯后他所能联通的联通块(即电梯处能无限制的到达其上下左右四个点,而这四个点周围和他高度差不超过d的点也可直达),并将这些点重新入队列即可,每个没处理过的点都可以建电梯。
代码
#include <bits/stdc++.h>
#define ll long long
#define mem( f, x ) memset( f, x, sizeof(f))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
#define pk push_back
using namespace std;
const int M = 35;
const int N = 35;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
int m, n, sx, sy, ex, ey, d;
int mp[N][N], dis[N][N];
int dir[4][2] = {
{
1,0}, {
-1,0}, {
0,1}, {
0,-1}};
pii vt[N*N];
int cnt;
queue<pii> q;
bool judge( int x, int y ){
if( x >= 0 && y >= 0 && x < m && y < n && dis[x][y] == INF )
return 1;
return 0;
}
void dfs( int x, int y ){
for( int i = 0; i < 4; i++ ){
int tx = x+dir[i][0];
int ty = y+dir[i][1];
if( judge( tx, ty ) && abs(mp[x][y]-mp[tx][ty])<=d ){
dis[tx][ty] = dis[x][y];
q.push( mk(tx,ty) );
dfs( tx, ty );
}
}
}
int main( ){
int t;
scanf( "%d", &t );
while( t-- ){
while( !q.empty() ) q.pop();
scanf( "%d %d %d %d %d %d %d", &n, &m, &sy, &sx, &ey, &ex, &d );
sx--, sy--, ex--, ey--;
for( int i = 0; i < m; i++ ){
for( int j = 0; j < n; j++ ){
scanf( "%d", mp[i]+j );
dis[i][j] = INF;
}
}
q.push( mk(sx,sy) );
dis[sx][sy] = 0;
dfs( sx, sy );
pii cur;
while( !q.empty() && dis[ex][ey] == INF ){
cnt = 0;
while( !q.empty() ){
vt[cnt++] = q.front();
q.pop();
}
for( int k = 0; k < cnt; k++ ){
cur = vt[k];
for( int i = 0; i < 4; i++ ){
int tx = cur.fi+dir[i][0];
int ty = cur.se+dir[i][1];
if( judge( tx, ty ) ){
q.push( mk(tx, ty) );
dis[tx][ty] = dis[cur.fi][cur.se]+1;
}
}
}
cnt = 0;
while( !q.empty() ){
vt[cnt++] = q.front();
q.pop();
}
for( int k = 0; k < cnt; k++ ){
cur = vt[k];
for( int i = 0; i < 4; i++ ){
int tx = cur.fi+dir[i][0];
int ty = cur.se+dir[i][1];
if( judge( tx, ty ) ){
dis[tx][ty] = dis[cur.fi][cur.se];
q.push( mk(tx, ty) );
dfs( tx, ty );
}
}
}
}
printf( "%d\n", dis[ex][ey] );
}
return 0;
}
I. 螺旋矩阵
leetcode螺旋输出的逆操作,比较坑的是,样例和图片中的数字标号完全对不上(不看图的估计正确率还能高一点)。最基础的先要算出输出矩阵的宽和高,分四种情况:
- 宽=高(1)宽=高=偶数 (2).宽=高=奇数
- 高=宽-1 (1)宽为奇数,高为偶数 (2)宽为偶数,高为奇数
四种情况的起点和方向变化不同,分别处理一下即可。另外字符串安排在矩阵中的位置也是逆序的。
代码
#include <bits/stdc++.h>
#define ll long long
#define mem( f, x ) memset( f, x, sizeof(f))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
#define pk push_back
using namespace std;
const int M = 800;
const int N = 800;
const int MOD = 1e9+7;
const ll INF = 1e18+50;
int m, n;
char mp[N][N];
bool vis[N][N];
int ex, ey;
int pos, len, sz;
string s;
int dir[4][4][2] = {
{
{
-1,0}, {
0,1}, {
1,0}, {
0,-1}},
{
{
0,-1}, {
-1,0}, {
0,1}, {
1,0}},
{
{
1,0}, {
0,-1}, {
-1,0}, {
0,1}},
{
{
0,1}, {
1,0}, {
0,-1}, {
-1,0}}};
bool judge( int x, int y){
if( x >= 1 && x <= m && y >= 1 && y <= n && !vis[x][y] )
return 1;
return 0;
}
int main( ){
int t;
scanf( "%d", &t );
while( t-- ){
mem( vis, 0 );
cin >> s;
len = s.length();
sz = sqrt(len*1.0);
if( sz*sz != len ) sz++;
pos = sz*(sz-1);
if( pos >= len )
m = sz-1, n = sz;
else
pos = sz*sz, m=n=sz;
int sx, sy;
int flag = 0;
if( !(m&1) && (n&1) ){
flag = 0;
sx = m, sy = 1;
}
if( (m&1) && (n&1) ){
flag = 1;
sx = m, sy = n;
}
if( (m&1) && !(n&1) ){
flag = 2;
sx = 1, sy = n;
}
if( !(m&1) && !(n&1) ){
flag = 3;
sx = 1, sy = 1;
}
if( pos > len )
mp[sx][sy] = ' ';
else
mp[sx][sy] = s[pos-1];
pos--;
int tx = sx, ty = sy;
vis[sx][sy] = 1;
while( pos ){
for( int i = 0; i < 4; i++ ){
while( judge(tx+dir[flag][i][0], ty+dir[flag][i][1]) ){
tx += dir[flag][i][0];
ty += dir[flag][i][1];
vis[tx][ty] = 1;
if( pos > len )
mp[tx][ty] = ' ';
else
mp[tx][ty] = s[pos-1];
pos--;
}
}
}
for( int i = 1; i <= m; i++ ){
for( int j = 1; j <= n; j++ )
cout << mp[i][j];
cout << endl;
}
cout << endl;
}
return 0;
}
J. 统计个数
N的范围只有200,T也只有50,C(N,3)的DFS暴力1e6左右,乘50不会爆。唯一坑的是,题目中关于三角形的定义描述真的是一言难尽,完全误导人的存在,和I题的图片坑比程度有的一拼。
代码
#include <bits/stdc++.h>
#define ll long long
#define mem( f, x ) memset( f, x, sizeof(f))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
#define pk push_back
using namespace std;
const int M = 205;
const int N = 205;
const int MOD = 1e9+7;
const ll INF = 1e18+50;
int m, n;
bool mp[N][N];
int num[3];
int ans1, ans2;
void dfs( int pos, int cnt ){
if( cnt == 3 ){
int a = num[0], b = num[1], c = num[2];
if( mp[a][b] && mp[b][c] ){
ans2++;
if( mp[a][c] )
ans1++;
}
if( mp[b][c] && mp[c][a] ){
ans2++;
if( mp[a][b] )
ans1++;
}
if( mp[c][a] && mp[a][b] ){
ans2++;
if( mp[b][c] )
ans1++;
}
return;
}
for( int i = pos+1; i <= n; i++ ){
num[cnt] = i;
dfs( i, cnt+1 );
}
}
int gcd( int x, int y ){
return !y ? x:gcd(y,x%y);
}
int main( ){
int t;
scanf( "%d", &t );
while( t-- ){
mem( mp, 0 );
scanf( "%d %d", &n, &m );
int x, y;
for( int i = 0; i < m; i++ ){
scanf( "%d %d", &x, &y );
mp[x][y] = mp[y][x] = 1;
}
ans1 = ans2 = 0;
dfs( 0, 0 );
int gd = gcd(ans2, ans1);
ans1 /= gd, ans2 /= gd;
printf( "%d/%d\n", ans1, ans2 );
}
return 0;
}