前言:第一题想复杂了,第二题犯了一个sb错误,第三题的话。。。
考虑这样一个斯诺克球台,它只有四个袋口,分别在四个角上(如下图所示)。我们把所有桌子边界上的整数点作为击球点(除了4个袋口),在每个击球点我们可以以45度角击球。
每一个击球点你都可以向两个方向击球,例如像下图所示,从S点击球有两种路线。提供桌子的尺寸,你的任务是计算出有多少种不同的击球方式使得球能入袋。球可视为质点,且无任何阻力,反弹时无能量损失。
仅一行,两个整数m和n,2 ≤ M,N ≤ 10^5,表示球台的长和宽。
符合以上描述的击球路线。
这题我写了bfs,用了1h+从四个角去搜索,模拟各种情况。可是这样做共有16中恶心的分类讨论,很烦。
其实还可以直接从一个角出发模拟球前进的情况,然后根据四个角的对称关系,乘以4就可以了。这样做是不可能遇到环的。
其实认真看,就可以发现样例都是4的倍数。
最后,做题一定不要把题想得太复杂,写这题浪费了我很多时间,导致后面两题失分惨重。
#include
#include
#include
#include
#include
#include
#define N 100005
using namespace std;
int m, n;
int head, tail;
int Ans;
bool f[4][N][4];
struct Data{
int x, y, dir;
Data() {}
Data(int x, int y, int dir):x(x), y(y), dir(dir) {}
}q[4*N*4];
bool In(int x, int y){
bool f1 = (x == 0 && y == 0) || (x == 3 && y == 0);
bool f2 = (x == 0 && y == n) || (x == 1 && y == 0);
bool f3 = (x == 1 && y == m) || (x == 2 && y == n);
bool f4 = (x == 2 && y == 0) || (x == 3 && y == m);
return f1 || f2 || f3 || f4;
}
int main(){
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
scanf("%d%d", &m, &n);
f[0][0][1] = f[1][0][3] = f[2][n][4] = f[3][m][2] = true;
head = tail = 0;
q[tail++] = Data(0, 0, 1);
q[tail++] = Data(1, 0, 3);
q[tail++] = Data(2, n, 4);
q[tail] = Data(3, m, 2);
while(head <= tail){
int x = q[head].x, y = q[head].y, dir = q[head].dir;
head ++;
int nx, ny, ndir;
if(x == 0){
if(dir == 1){
if(y + m <= n) nx = 2, ny = y + m, ndir = 2;
else nx = 1, ny = n - y, ndir = 3;
}
else{
if(y >= m) nx = 2, ny = y - m, ndir = 4;
else nx = 3, ny = y, ndir = 1;
}
}
else if(x == 1){
if(dir == 3){
if(y + n <= m) nx = 3, ny = y + n, ndir = 1;
else nx = 2, ny = n - m + y, ndir = 4;
}
else{
if(y <= n) nx = 0, ny = n - y, ndir = 3;
else nx = 3, ny = y - n, ndir = 2;
}
}
else if(x == 2){
if(dir == 4){
if(y >= m) nx = 0, ny = y - m, ndir = 3;
else nx = 3, ny = m - y, ndir = 2;
}
else{
if(y + m <= n) nx = 0, ny = y + m, ndir = 1;
else nx = 1, ny = m - n + y, ndir = 4;
}
}
else{
if(dir == 1){
if(y + n <= m) nx = 1, ny = y + n, ndir = 3;
else nx = 2, ny = m - y, ndir = 2;
}
else{
if(y <= n) nx = 0, ny = y, ndir = 1;
else nx = 1, ny = y - n, ndir = 4;
}
}
if(!In(nx, ny) && !f[nx][ny][ndir]){
f[nx][ny][ndir] = true;
q[++tail] = Data(nx, ny, ndir);
Ans ++;
}
}
printf("%d\n", Ans);
return 0;
}
排列,相信大家都很熟悉了,给你0 到N-1,共 N个数, 那么在排列中,每个数都要出现,而且只出现一次. 对于一个排列A, 有一个与之对应的孩子序列B, 其中B这样定义的:
1、B[0] = 0
2、B[i] = A[B[i-1]], 1 <= i <= N-1.
对于一个排列X, 如果它的孩子序列也是一个排列, 那么X称为“完美排列”。
看下面的例子,N=3时,有6个排列,其中{1, 2, 0}、{2, 0, 1}这两个排列的孩子序列也是一个排列,所有{1, 2, 0}、{2, 0, 1}是完美排列。
给你一个整数N,再给出它的一个排列P,要你找一个完美排列Q,使得Q跟P的差距dif最小,两个排列的差距dif是这样定义的:对于每个下标i,如果P[i] 不等于 Q[i],那么dif加1, 0 <= i < N; 如果Q不唯一,那么请输出孩子序列中字典序最小的Q. 也就是在所有满足题意的完美序列中,看哪个完美序列的孩子序列字典序最小,输出该完美序列。
多组测试数据,第一行:一个整数ng,表示有1<=ng<=5组测试数据。
每组测试数据格式如下:
第一行:一个整数N, 表示排列的长度。1 <= N <= 50.
第二行:有N个整数,空格分开,表示给出的一个排列,就是对应的P[ ]数组。
ng行,每行对应一组测试数据。 每一行,就是满足题意的Q, 共N个整数, 空格分开。
2
3
2 0 1
6
4 0 5 2 1 3
2 0 1
2 0 5 4 1 3
这题算是我这场比赛最可惜的一道题。
首先题面长如狗,将题意转换,i向P[i]连边成环。考场上我已经将一个个环找出来了,然后也知道连边,甚至连贪心都写好了,结果一测试就爆零了。
原因竟然是:连边连多了!导致了字典序更优但dif增加了!n个环连成一个大的,应该连几条边呢?将环画横排在纸上的naive的我,天真的想,当然是(n-1)*2条了。结果,显然的,只需要n条边。
于是我贪心就理所应得的错了。
一开始错误的作法是在环上选最小的,再连其他的最小的,再连。。
实际上,只有第一个环(就是0在的那个环)需要向后判断是否连出去,其他的由于受到dif最小的限制只能直接走完整个环。贪心地按环上的最小值排序后,这就变成非常简单的模拟了。
ps:据说kxy和KsCla看错了题,将孩子序列字典序最小看成了Q的字典序最小。。。那个贪心就比较难写了。。。。
#include
#include
#include
#include
#include
#include
#define N 60
#define oo 0x7fffffff
using namespace std;
int nG, Cnt;
int n, Ring[N], P[N], Pre[N], Next[N];
bool vis[N];
int main(){
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
scanf("%d", &nG);
while(nG --){
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &P[i]);
for(int i = 0; i < n; i++){
Next[i] = P[i];
Pre[P[i]] = i;
}
for(int i = 0; i < n; i++) vis[i] = false;
Cnt = 0;
for(int i = 0; i < n; i++){
if(vis[i]) continue;
int Min = oo, j = i;
do{
vis[j] = true;
Min = min(Min, j);
j = Next[j];
}while(j != i);
Ring[++Cnt] = Min;
}
sort(Ring+1, Ring+Cnt+1);
int now = Ring[1];
while(Next[now] != Ring[1] && Next[now] < Ring[2]) now = Next[now];
now = Next[now];
Ring[1] = now;
for(int i = 1; i <= Cnt; i++){
now = Pre[Ring[i]];
Next[now] = Ring[i%Cnt+1];
}
for(int i = 0; i < n; i++) printf("%d ", Next[i]);
printf("\n");
}
return 0;
}
FJ最近很烦恼,因为他正在寻找一些拼图块,这些拼图块其实可以拼成N个有顺序的完整的拼图。每个完整的拼图由若干个拼图块组成。
FJ希望把这些完整的拼图按拼出的顺序划分成M个集合,一个拼图集合由若干个完整的拼图组成,并且每个集合总的拼图块的数目不超过T。
并且,构成集合的拼图是不能交叉的,例如,当拼图1与拼图3被放在拼图集合1中之后,拼图2就只能放进拼图集合1或者不放进任何拼图集合。
FJ要找出划分成M个集合后,M个集合中最多能有多少个完整的拼图。
第一行为三个整数,为N,M,T(1 <= N,M,T <= 1000)
第二行有N个数,按照拼出拼图的顺序给出N个拼图分别含有多少个拼图块(拼图块的个数是不超过T的正整数)。
输出文件只有一个数字,为M个拼图集合最多包含的完整拼图个数
6 2 4
1 1 3 1 2 2
输出样例
5
样例解释:
对于样例数据,1个可行的方案如下:
拼图集合1放拼图1和拼图2和拼图4,
拼图集合2放拼图5和拼图6
于是最多可以放5个拼图
并且显然不存在能够放5个以上拼图的方案
【数据规模】
对于30%的数据,有1<=N,M<=100
对于100%的数据,有1<=N,M<=1000
这题一看就是类似于背包模型的dp。
如果记三维状态,无论是枚举上一个点,还是直接记多一维做背包,时间都是不可接受的 O(n3) 。
于是有优化?
这题直接记二元组。类似于今年KOI Day1 T4那题dp,当时没做出来,后来听讲后又没有认真总结和修改,导致考试时完全没有印象。
所以总结经验,改正错误很重要啊。
然后知道记二元组后的我,一开始是这么想的,记 f[i][j]={k,x} 表示第到了第 i 个集合,第 j 个拼图,最后一个集合剩下 k 的空间,答案为 x 。由于我以前没听课,苦思冥想,结果KsCla告诉我这是不行的,因为二元组必须满足第一关键字的优先级大于第二关键字。
就是说,我记得两个值 k,x 没有任何可比性,不能说 x 大, k 小就认为比 x 小 k 大的状态优。我恍然大悟,我们不能直接记答案。转而记 f[i][j]={k,x} ,表示做到第 i 个拼图,选了 j 个(这是很常见的模型),然后用了 k 个集合,最后一个集合用了 x 的空间。这就很科学了,因为如果同样的状态,如果 k 小肯定更优, k 一定才有比 x 的必要。所以这就是二元组的特点。
然后方程就很简单的背包模型,选or不选,然后转移。
即
一开始不取是 {k=1,x=0} ,其他都设为很大,最后集合小于 m 的答案贡献给 Ans 就行了。
时间 O(n2) 。
dp真是我的硬伤,遇见过的题目一定要熟练,主要还是时间不够,暴力都没写。
#include
#include
#include
#include
#include
#include
#define N 1010
using namespace std;
int n, m, T, ans;
int a[N];
struct Data{
int x, y;
Data() {}
Data(int x, int y):x(x), y(y) {}
}f[N][N];
Data put(Data A, int v){
A.y += v;
if(A.y > T) A.y = v, A.x ++;
return A;
}
Data Min(Data A, Data B){
if(A.x == B.x) return A.y < B.y ? A : B;
return A.x < B.x ? A : B;
}
int main(){
freopen("c.in", "r", stdin);
freopen("c.out", "w", stdout);
scanf("%d%d%d", &n, &m, &T);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 0; i <= n; i++)
for(int j = 0; j <= n; j++) f[i][j] = Data(m+1, 0);
for(int i = 0; i <= n; i++) f[i][0] = Data(1, 0);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
f[i][j] = Min(f[i][j], Min(f[i-1][j], put(f[i-1][j-1], a[i])));
ans = 0;
for(int i = 1; i <= n; i++) if(f[n][i].x <= m) ans = i;
printf("%d\n", ans);
return 0;
}
蒟蒻的我也要翻身啊,不行啊,还是太菜了。。。。
考试时间安排上很难受,调试程序的能力很弱,导致很多题想到了却调试不出来。
写程序一定要认真,提高码代码的能力实在太重要了。
脑子不要坏啊。
为什么天空如此蔚蓝
仿佛一点悲伤也不懂似的