Benelux Algorithm Programming Contest 2014 Final
G - Growling Gears
找 最大的就可以
B - Button Bashing
这题当时想多了,第一反应是一个背包,如果这题没有上下界的限制的话,用背包其实是可以的,可以把按键增加的时间从大到小排序,让加时的先做,减时的后做背包,这样都不用把容量加上3600了。
但其实这题用spfa做就可以,因为n是在太小了。。。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 20;
int v[MAXN];
int n,t;
int vis[4000];
bool inque[4000];
void spfa()
{
memset(vis,0x3f,sizeof vis);
queue<int>que;
vis[0] = 0;
que.push(0);
while (!que.empty())
{
int u = que.front();que.pop();
inque[u] = false;
for (int i=1;i<=n;i++)
{
int to = u + v[i];
if (to > 3600) to = 3600;
else if (to < 0) to = 0;
if (vis[to] > vis[u] + 1)
{
vis[to] = vis[u] +1 ;
if (!inque[to])
{
que.push(to);
inque[to] = true;
}
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&t);
for (int i=1;i<=n;i++) scanf("%d",&v[i]);
spfa();
for (int i=t;i<=3600;i++)
{
if (vis[i] != 0x3f3f3f3f)
{
printf("%d %d\n",vis[i],i - t);
break;
}
}
}
return 0;
}
J - Jury Jeopardy
用一个大点的地图,把所有走过的路都用.标出来,其他地方都用#表示,找出一个上右下侧都是#的矩形,并且包围那个路径就可以了。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 210;
char mp[400][400];
string s;
int main()
{
int t;
cin >> t;
while (t--)
{
cin >> s;
memset(mp,0,sizeof mp);
int x = 200,y = 1,face = 0;
mp[x][y] = '.';
for (int i=0;i<s.length();i++)
{
if (s[i] == 'B')face = (face + 2)%4;
if (s[i] == 'R') face = (face+1)%4;
if (s[i] == 'L') face = ((face-1)%4+4)%4;
if (face == 0) y++;
if (face == 1) x++;
if (face == 2) y--;
if (face == 3) x--;
mp[x][y] = '.';
}
int stx = 0,edx = 0,sty = 0,edy = 0;
for (int i=1;i<=400 && !stx;i++)
{
for (int j=1;j<=400;j++)
if (mp[i][j] =='.')
{
stx = i-1;
break;
}
}
for (int i=stx+1;i<=400;i++)
{
bool flag = false;
for (int j=1;j<=400;j++)
if (mp[i][j] == '.') flag = true;
if (flag == false)
{
edx = i;
break;
}
}
for (int i=1;i<=400;i++)
{
bool flag = false;
for (int j=1;j<=400;j++)
if (mp[j][i] == '.') flag = true;
if (!flag)
{
edy = i;
break;
}
}
printf("%d %d\n",edx-stx+1,edy);
for (int i=stx;i<=edx;i++)
{
for (int j=1;j<=edy;j++)
{
if (mp[i][j] == 0) mp[i][j] = '#';
printf("%c",mp[i][j]);
}
printf("\n");
}
}
return 0;
}
I - Interesting Integers
一个数字由a,b为初始的fibonacci数列组成
列一下算式就会发现,这个数字是由a*x + b*y组成,并且x,y是两个相邻fibonacci数,由于最大就1e9,所以枚举一下也很快,可以从y较大开始枚举,这样b的范围就会变小。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 101000;
const int INF = 0x3f3f3f3f;
int fib[MAXN];
int main()
{
fib[1] = 1;
fib[2] = 1;
for (int i=3;i<=44;i++) fib[i] = fib[i-2] + fib[i-1];
int t,n;
cin >> t;
while (t--)
{
cin >> n;
int gcd,x,y;
int ansx = INF,ansy = INF;
for (int i=44;i>=2;i--)
{
if (fib[i] > n) continue;
long long a = fib[i-1],b = fib[i];
for (int j=1;j<=ansy;j++)
{
if ( b * j >= n) break;
if ( (n - b*j) % a == 0)
{
int tmpx = (n - b*j) / a;
if (tmpx > j) continue;
if (j < ansy)
{
ansy = j;
ansx = tmpx;
}else if ( j == ansy && tmpx < ansx)
{
ansx = tmpx;
ansy = j;
}
}
}
}
printf("%d %d\n",ansx,ansy);
}
return 0;
}
E - Excellent Engineers
比赛的时候想到了先把第一维排个序,然后从第一维的第一名到第n名这样考虑,所以要维护一个2维树状数组,来判断第二维第三维都比你小的有没有,但是明显空间不够。然后就卡死了。。
看题解后知道了要维护一个叫做Pareto front 的东西,如下图,我们要维护的是下面的一条,在黄色区域的代表不能选择的点,准确来说,这些点要在e之后放入图中,才不能选择,否则也是可以选的,因为如果在e之前放入,说明它们的第一维比e要小。如果是在e之前放入,这些可能被选,但是更新pareto front的时候这些点是要删掉的,也就是它们在可能在名单上,但是肯定不在pareto front里面。
先看x坐标,代表的是第二维的排名,y坐标代表第三维排名,可以看出,如果有人的x坐标比你小,并且y也比你小,那么你肯定就不能在pareto front了,所以这条线从左到右是一个递降的趋势。
那么维护的时候,先判断是否需要放入,如果需要放入,那么再把原来在集合中两个维度都大于它的删掉,然后再插入它,
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100010;
struct node
{
int a,b,c;
}p[MAXN];
bool cmp1(const node &a,const node &b)
{
return a.a < b.a;
}
bool cmp2(const node &a,const node &b)
{
return a.b < b.b;
}
typedef set<node,bool(*)(const node &,const node &)> myset;
typedef myset::iterator IT;
int n;
int main()
{
int t;
cin >> t;
while (t--)
{
int ans = 0;
myset e1(cmp1),e2(cmp2);
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c);
e1.insert(p[i]);
}
for (IT it1 = e1.begin();it1!=e1.end();it1++)
{
bool putin = false;
IT it2 = e2.lower_bound(*it1);
if (it2 == e2.begin()) putin = true;
else
{
it2--;
if (it1->c < it2->c ) putin = true;
it2++;
}
if (!putin) continue;
IT a = it2,b = it2;
while (b!=e2.end() && b->c > it1->c)b++;
e2.erase(a,b);
e2.insert(*it1);
ans++;
}
printf("%d\n",ans);
}
return 0;
}
D - Dropping Directions
这题当时是想到找环了,但是具体怎么处理没想好。
仔细一想的话会发现,这个图上的环是在太多了,感觉都可以当成,一个球体表面上切成了一个个矩形一样来看了。每条边只有两种走法,也就是顺逆。我一开始还在想会不会两个环重叠,这样只要一个指示牌就可以了,但举了举例子后发现这是不可能的,因为只要重合了,他们的走法就肯定会一样,所以不可能走出两个环来。那么只要根据他给的图,把所有路都走一遍,数一下有几个不包含g的环就可以了。
看了这题,我感觉做题目的时候还是要学会看出题的特殊性,找出图的特殊性,然后从这点下手去解决问题。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100100;
int n,g,t;
int dir[MAXN][4];
bool vis[MAXN][4];
int main()
{
scanf("%d",&t);
while (t--)
{
memset(vis,0,sizeof vis);
scanf("%d%d",&n,&g);
for (int i=1;i<=n;i++)
for (int j=0;j<4;j++)
scanf("%d",&dir[i][j]);
int post = 0;
for (int i=1;i<=n;i++)
for (int j=0;j<4;j++)
if (!vis[i][j])
{
int curi = i,curj = j;
bool foundg = false;
while (1)
{
if (curi == g) foundg = true;
int nexi = dir[curi][ (curj + 2)%4 ];
int nexj;
for (int k=0;k<4;k++) if ( dir[nexi][k] == curi ) nexj = k;
if (vis[curi][curj] && vis[curi][ (curj + 2)%4 ]) break;
vis[curi][curj] = true;
vis[curi][ (curj + 2)%4 ] = true;
curi = nexi;
curj = nexj;
}
if (!foundg)
post ++;
}
printf("%d\n",post);
}
return 0;
}