练习赛1
- Problem A Before an Exam (水题)
- Problem B The least round way (DP)
- Problem C Tic-tac-toe (模拟)
- Problem D Ancient Berland Circus (几何)
- Problem E Least Cost Bracket Sequence (贪心)
Problem A Before an Exam
给定 d 天,每天可以学习 [mini,maxi] 个小时,父母要求的是sumtime个小时,上下限都要考虑下。 sumt 能否合理安排每天的学习,使得满足要求。
思路
求一下最小值和,最大值和,如果sum在之间,一定满足。
AC代码
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
int d, sumtime, maxn, minn, x[100], y[100];
cin >> d >> sumtime;
maxn = 0;
for(int i = 0; i < d; i++)
{
cin >> x[i] >> y[i];
minn += x[i];
maxn += y[i];
}
if(maxn < sumtime || minn > sumtime)
{
printf("NO\n");
return 0;
}
int delta = maxn - sumtime;
for(int i = 0; i < d; i++)
{
if(y[i] - x[i] >= delta)
{
y[i] -= delta;
break;
}
else
{
delta -= y[i] - x[i];
y[i] = x[i];
}
}
printf("YES\n");
for(int i = 0; i < d; i++)
printf("%d ", y[i]);
return 0;
}
Problem B The least round way
n × n 的矩阵,每个格子上有一个数字。现在从左上角走到右下角,每次只能向右、向下走,每走到一格,乘上该格子上的数字。问怎么走,能够使得这些数字的乘积末尾0最少。给出最少的0和路径。
思路
对于每一个数字,进行处理,我们只需要知道这个数字分别能被2、5整除多少次。
第一次走的时候,保证取2最小,(2一样的时候,5要最小),且在走的过程中记录路径。
然后再走一次,保证取5最小,(5一样的时候,2要最小),且在走的过程中记录路径。
最后的答案就是上面两个的最小值。
trick:有可能有数字为0,那乘积为0,答案为1,否则上述得到的最小值大于1,那么便不取,而是选择走0那条路径。
AC代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#include <functional>
#include <stdarg.h>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-4)
#define inf (1<<28)
#define ll long long int
using namespace std;
int mat[1005][1005];
char direct[1005][1005][2]; //分别记录2,5整除次数最少的路径用D,R表示
int dp[1005][1005][2]; //分别记录矩阵中每个数能被2,5整除的次数
char path[2010];
int path_len;
int factors(int num, int base)
{
int ret = 0;
int fct = base;
if(num == 0)
return 1;
while(num % fct == 0)
{
ret++;
fct *= base; //除数乘以base和被除数除以base一样
}
return ret;
}
int mi(int a, int b)
{
return a < b ? a : b;
}
int main()
{
int n;
scanf("%d", &n);
bool zero = false; //判断矩阵中是否有0
int zero_i;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
scanf("%d", &mat[i][j]);
if(mat[i][j] == 0)
{
zero = true;
zero_i = i; //记录其中一个0所在的行
}
dp[i][j][0] = factors(mat[i][j], 2);
dp[i][j][1] = factors(mat[i][j], 5);
}
}
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
for(int k = 0; k < 2; k++) //记录从起点到该点的最小次数,同时记录路径
{
if(i == 0 && j == 0) continue;
if(i == 0)
{
dp[i][j][k] += dp[i][j - 1][k];
direct[i][j][k] = 'R';
}
else if(j == 0)
{
dp[i][j][k] += dp[i - 1][j][k];
direct[i][j][k] = 'D';
}
else
{
dp[i][j][k] += dp[i - 1][j][k] < dp[i][j - 1][k] ? dp[i - 1][j][k] : dp[i][j - 1][k];
direct[i][j][k] = dp[i - 1][j][k] < dp[i][j - 1][k] ? 'D' : 'R';
}
}
}
}
if(mi(dp[n - 1][n - 1][0], dp[n - 1][n - 1][1]) > 1 && zero)
{
printf("1\n");
for(int i = 0; i < zero_i; i++) printf("D");
for(int i = 0; i < n - 1; i++) printf("R");
for(int i = zero_i; i < n - 1; i++) printf("D");
}
else
{
printf("%d\n", mi(dp[n - 1][n - 1][0], dp[n - 1][n - 1][1]));
path_len = 0;
int k = 1;
if(dp[n - 1][n - 1][0] < dp[n - 1][n - 1][1]) k = 0;
for(int i = n - 1, j = n - 1; i != 0 || j != 0; )
{
path[path_len++] = direct[i][j][k];
if(direct[i][j][k] == 'D')
{
i--;
}
else if(direct[i][j][k] == 'R')
{
j--;
}
}
for(int i = 2 * (n - 1) - 1; i >= 0; i--)
printf("%c", path[i]);
}
printf("\n");
return 0;
}
Problem C Tic-tac-toe
3 X 3的棋盘,井字棋游戏,两个人连续在棋盘上放棋子(第一个人为X,第二个人为0),如果出现某行某列或者对角线上全为同样的棋子,那么该人获胜。
现在给出当下棋盘状态,让判断是那种状态
- illegal 棋盘情况不合法
- the first player won 第一个人赢
- the second player won 第二个人赢
- draw 平局
- first 轮到第一个人走
- second 轮到第二个人走 -
思路
主要illegal的情况比较多:
- 双方棋子数不符合规则
- 双方都赢了
- 先手赢了,后手还放棋子
- 后手赢了,先手还放棋子
AC代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-4)
#define inf (1<<28)
#define ll long long int
using namespace std;
int xcnt, ocnt, num3;
char gao[3][3];
bool win(char tmp) //用函数表示会简单一些,我之前写的太挫了
{
for(int i = 0; i < 3; i++)
{
if(gao[i][0] == tmp && gao[i][1] == tmp && gao[i][2] == tmp) return true;
if(gao[0][i] == tmp && gao[1][i] == tmp && gao[2][i] == tmp) return true;
}
if(gao[1][1] != tmp) return false;
if(gao[0][0] == tmp && gao[2][2] == tmp) return true;
if(gao[2][0] == tmp && gao[0][2] == tmp) return true;
return false;
}
bool legal()
{
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
xcnt += gao[i][j] == 'X';
ocnt += gao[i][j] == '0';
}
}
if(xcnt < ocnt || xcnt - ocnt > 1) return false;
if(win('X') && xcnt == ocnt) return false;
if(win'X' && win'0') return false;
if(win('0') && xcnt != ocnt) return false;
return true;
}
int main()
{
xcnt = 0;
ocnt = 0;
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
cin >> gao[i][j];
if(!legal()) //判断非法
{
printf("illegal\n");
return 0;
}
if(win('X')) //判断赢家
{
printf("the first player won\n");
return 0;
}
if(win('0'))
{
printf("the second player won\n");
return 0;
}
if(xcnt + ocnt == 9) //判断平局
{
printf("draw\n");
return 0;
}
if(xcnt == ocnt) //判断先后手
{
printf("first\n");
return 0;
}
if(xcnt - ocnt == 1)
{
printf("second\n");
return 0;
}
}
Problem D Ancient Berland Circus
几何题,有一个正多边形(3<=n<=100),给出其中的三个顶点,问这个正多边形最小可能的面积。
思路
最小面积的话,就是要求正多边形的边数尽量少,给出三个点,可以求出外接圆
AC代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-4)
#define inf (1<<28)
#define ll long long int
using namespace std;
double a, b, c, r, q, s;
double A, B, C, angle, ans, n;
struct node
{
double x, y;
}piller[3];
double dis(node a, node b) //计算两点间的距离
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double fgcd(double a, double b) //实型求余fmod函数,fgcd求出实数a,b的(近似)最大公约数
{
if(fabs(b - 0) <= eps)
return a;
return fgcd(b, fmod(a, b));
}
void init()
{
memset(piller, 0, sizeof(piller));
for(int i = 0; i < 3; i++)
{
scanf("%lf%lf", &piller[i].x, &piller[i].y);
}
a = dis(piller[0], piller[1]);
b = dis(piller[1], piller[2]);
c = dis(piller[2], piller[0]);
q = (a + b + c) / 2; //海伦公式
s = sqrt(q * (q - a) * (q - b) * (q - c));
r = a * b * c / (4 * s);
A = acos(1 - a * a / r / r / 2);
B = acos(1 - b * b / r / r / 2);
C = 2 * pi - A - B;
}
int main()
{
init();
ans = pi / fgcd(A, fgcd(B, C)) * r * r * sin(fgcd(A, fgcd(B, C)));
printf("%.8lf\n", ans);
return 0;
}
Problem E Least Cost Bracket Sequence
有一串字符串,每个字符为’(‘, ‘)’ 或’?’。现在需要将其中的’?’替换为’(‘或’)’,使得最后所有括号能匹配。每个’?’替换成相应的括号都有一个花费。如果最后不能匹配,输出-1。否则输出最少的花费。
思路
优先队列的用法,cnt 记录了‘(’和‘)’的匹配状况,‘?’先当作‘)’处理,遇到‘(’cnt++,遇到')'cnt--;遇到‘?’时cnt--,同时输入两个取代值a,b,并将b - a,和当前位置配对存入优先队列 ,如果遇到cnt<0时,队列中踢出一组 即 之前当作‘)’处理的‘?’现在当作‘(’处理,cnt += 2,同时ss权值减去此处a, b 的差;如果此时没有备用的‘?’则不符合要求,输出-1;
AC代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-8)
#define inf 0x3f3f3f3f
#define ll long long int
using namespace std;
#define MAXN 50000+10
char s[MAXN];
ll ss;
int cnt;
int a, b;
priority_queue<pair<ll, int> > que;
int main()
{
scanf("%s", s + 1);
ss = 0;
cnt = 0;
for(int i = 1; s[i]; i++)
{
if(s[i] == '(')
{
cnt++;
}
else if(s[i] == ')')
{
cnt--;
}
else
{
scanf("%d%d", &a, &b);
ss += b;
cnt--;
s[i] = ')';
que.push(pair<ll, int> (b - a, i));
}
if(cnt < 0)
{
if(que.empty()) break;
pair<ll, int> f = que.top();
que.pop();
ss -= f.first;
cnt += 2;
s[f.second] = '(';
}
}
if(cnt != 0)
printf("-1\n");
else
printf("%lld\n%s\n", ss, s + 1);
return 0;
}