2014 Benelux Algorithm Programming Contest (BAPC 14) 部分题解

太弱了。只能做一半 _(:3∠)_

B - Button Bashing (bfs)

题意

问调到目标值的最小步,不然就输出最接近的。

思路

一开始不知道怎么就想到DP去了,先DP出正的,然后DP负的,还一直以为这样是对的。

bfs搞搞。

代码

#include <stack>
#include <cstdio>
#include <list>
#include <cassert>
#include <set>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <functional>
#include <cstring>
#include <algorithm>
#include <cctype>
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <string>
#include <map>
#include <cmath>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
//using namespace __gnu_pbds;
#define LL long long
#define ULL unsigned long long
#define SZ(x) (int)x.size()
#define Lowbit(x) ((x) & (-x))
#define MP(a, b) make_pair(a, b)
#define MS(arr, num) memset(arr, num, sizeof(arr))
#define PB push_back
#define X first
#define Y second
#define ROP freopen("input.txt", "r", stdin);
#define MID(a, b) (a + ((b - a) >> 1))
#define LC rt << 1, l, mid
#define RC rt << 1|1, mid + 1, r
#define LRT rt << 1
#define RRT rt << 1|1
#define FOR(i, a, b) for (int i=(a); (i) < (b); (i)++)
#define FOOR(i, a, b) for (int i = (a); (i)<=(b); (i)++)
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const double eps = 1e-4;
const int MAXN = 3e5+10;
const int MOD = 10007;
const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
const int seed = 131;
int cases = 0;
typedef pair<int, int> pii;

queue<pii> Q;
int vis[10000], tar;
vector<int> arr;

void bfs()
{
    Q.push({0, 0});
    while (!Q.empty())
    {
        pii u = Q.front(); Q.pop();
        for (int i = 0; i < SZ(arr); i++)
        {
            int v = u.X + arr[i];
            if (v > 3600) v = 3600;
            if (vis[v] != INF || v <= 0) continue;
            vis[v] = u.Y+1;
            Q.push({v, u.Y+1});
        }
    }
    if (vis[tar] != INF) printf("%d 0\n", vis[tar]);
    else for (int i = tar+1; i <= 3600; i++)
        if (vis[i] != INF)
        {
            printf("%d %d\n", vis[i], i-tar);
            return;
        }
}

int main()
{
    //ROP;
    //freopen("out.txt", "w", stdout);
    int T;
    scanf("%d", &T);
    while (T--)
    {
        arr.clear();
        MS(vis, INF);
        vis[0] = 0;
        int n;
        scanf("%d%d", &n, &tar);
        for (int i = 0; i < n; i++)
        {
            int tmp;
            scanf("%d", &tmp);
            arr.PB(tmp);
        }
        bfs();
    }
    return 0;
}

E - Excellent Engineers (线段树)

题意

如果一个人rank都比另一个人高,就可以干掉另一个人。

问最后有多少人能存活。

思路

显然最后存活的数和输入顺序无关。

假设三个属性分别为x、y、z。

我们可以先按x排序。

线段树的区间表示的是y,线段树维护的是某个范围内的y对应z的最小值。

之后假设我们现在要插入第x个人。

显然他不能消灭掉前x-1个人,因为前面人至少x的rank比他高。所以我们考虑他能不能存活下来。

假设第x个人的y=y0, z=z0。

如果存在一个人,使得 y<y0andz<z0 ,那么第x个人就活不下来。否则就可以。

所以我们可以在线段树的(1~y0-1)区间内查询它的最小值,如果最小值 <z0 <script type="math/tex" id="MathJax-Element-6">

代码

#include <stack>
#include <cstdio>
#include <list>
#include <cassert>
#include <set>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <functional>
#include <cstring>
#include <algorithm>
#include <cctype>
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <string>
#include <map>
#include <cmath>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
//using namespace __gnu_pbds;
#define LL long long
#define ULL unsigned long long
#define SZ(x) (int)x.size()
#define Lowbit(x) ((x) & (-x))
#define MP(a, b) make_pair(a, b)
#define MS(arr, num) memset(arr, num, sizeof(arr))
#define PB push_back
#define X first
#define Y second
#define ROP freopen("input.txt", "r", stdin);
#define MID(a, b) (a + ((b - a) >> 1))
#define LC rt << 1, l, mid
#define RC rt << 1|1, mid + 1, r
#define LRT rt << 1
#define RRT rt << 1|1
#define FOR(i, a, b) for (int i=(a); (i) < (b); (i)++)
#define FOOR(i, a, b) for (int i = (a); (i)<=(b); (i)++)
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const double eps = 1e-4;
const int MAXN = 1e5+10;
const int MOD = 10007;
const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
const int seed = 131;
int cases = 0;
typedef pair<int, int> pii;

struct POINT
{
    int x, y, z;
    bool operator < (const POINT &a) const
    {
        return x < a.x;
    }
}p[MAXN];

struct TREE
{
    int min_val;
}t[MAXN<<2];

void push_up(int rt)
{
    t[rt].min_val = min(t[LRT].min_val, t[RRT].min_val);
}

void Update(int rt, int l, int r, int y, int z)
{
    if (l == r)
    {
        t[rt].min_val = z;
        return;
    }
    int mid = MID(l, r);
    if (y <= mid) Update(LC, y, z);
    else Update(RC, y, z);
    push_up(rt);
}

int Query(int rt, int l, int r, int L, int R)
{
    int ret = INF;
    if (L <= l && r <= R) return t[rt].min_val;
    int mid = MID(l, r);
    if (L <= mid) ret = min(ret, Query(LC, L, R));
    if (R >= mid+1) ret = min(ret, Query(RC, L, R));
    return ret;
}

int main()
{
    //ROP;
    int T;
    scanf("%d", &T);
    while (T--)
    {
        MS(t, INF);
        int n;
        scanf("%d", &n);
        FOR(i, 0, n) scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].z);
        sort(p, p+n);
        int ans = 0;
        for (int i = 0; i < n; i++)
        {
            if (p[i].y != 1)
            {
                int tmp = Query(1, 1, n, 1, p[i].y-1);
                if (tmp < p[i].z) continue;
            }
            ans++;
            Update(1, 1, n, p[i].y, p[i].z);
        }
        printf("%d\n", ans);
    }
    return 0;
}

I - Interesting Integers (扩展欧几里得)

题意

可以定义Fibonacci数列的前两项,现在给出n,问满足题目要求的最小的两项。

思路

先仰慕illuz巨菊!

一开始老是想着分解n成两项之和,一直没思路。

然后请教了hcbbt巨巨。

我们知道,<=1e9的Finobacci数列只有50项。而且每一项的系数都是Fibonacci数列。

假设系数是x、y。

那么我们就是求ax+by = n的最小a、b。

然后就可以用扩展欧几里得搞了。

又加深了对扩展欧几里得的理解= =

代码

#include <stack>
#include <cstdio>
#include <list>
#include <cassert>
#include <set>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <functional>
#include <cstring>
#include <algorithm>
#include <cctype>
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <string>
#include <map>
#include <cmath>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
//using namespace __gnu_pbds;
#define LL long long
#define ULL unsigned long long
#define SZ(x) (int)x.size()
#define Lowbit(x) ((x) & (-x))
#define MP(a, b) make_pair(a, b)
#define MS(arr, num) memset(arr, num, sizeof(arr))
#define PB push_back
#define X first
#define Y second
#define ROP freopen("input.txt", "r", stdin);
#define MID(a, b) (a + ((b - a) >> 1))
#define LC rt << 1, l, mid
#define RC rt << 1|1, mid + 1, r
#define LRT rt << 1
#define RRT rt << 1|1
#define FOR(i, a, b) for (int i=(a); (i) < (b); (i)++)
#define FOOR(i, a, b) for (int i = (a); (i)<=(b); (i)++)
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const double eps = 1e-4;
const int MAXN = 3e5+10;
const int MOD = 10007;
const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
const int seed = 131;
int cases = 0;
typedef pair<LL, LL> pii;

void extend_gcd(LL a, LL b, LL &d, LL &x, LL &y)
{
    if (!b) d = a, x = 1, y = 0;
    else
    {
        extend_gcd(b, a%b, d, y, x);
        y -= x * (a/b);
    }
}

pii ans;

void Solve(LL x, LL y, LL a, LL b)
{
    LL k = -x / b;
    x += k*b, y -= k*a;
    if (x <= 0)
    {
        while (x <= 0) x += b, y -= a;
        if (y < x) return;
    }
    else
    {
        while (x - b > 0) x -= b, y += a;
        if (y < x) return;
    }
    k = (y-x-1) / (a+b);
    x += b*k, y -= a*k;
    while (y >= x && y <= ans.Y)
    {
        if (y < ans.Y || (y == ans.Y && x < ans.X)) ans.X = x, ans.Y = y;
        x += b, y -= a;
    }
}

int arr[200];

int main()
{
    //ROP;
    int T;
    scanf("%d", &T);
    arr[1] = arr[2] = 1;
    for (int i = 3; i <= 50; i++) arr[i] = arr[i-1] + arr[i-2];
    while (T--)
    {
        ans.X = INF, ans.Y = INF;
        int n;
        scanf("%d", &n);
        for (int i = 2; i <= 43; i++)
        {
            int a = arr[i-1], b = arr[i];
            if (n % __gcd(a, b) != 0) continue;
            LL d, x, y;
            extend_gcd(a, b, d, x, y);
            a /= d, b /= d;
            x *= n, y *= n;
            Solve(x, y, a, b);
        }
        cout << ans.X << " " << ans.Y << endl;
    }
    return 0;
}

J - Jury Jeopardy (模拟)

题意

让我们根据序列输出图。

思路

首先我们知道没有比起点更小的列。不然起点就会被封死。

模拟一下。先把所有的.给记上,然后记录一下行列的最大值和最小值,然后一排一排补全即可。

代码

#include <stack>
#include <cstdio>
#include <list>
#include <cassert>
#include <set>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <functional>
#include <cstring>
#include <algorithm>
#include <cctype>
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <string>
#include <map>
#include <cmath>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
//using namespace __gnu_pbds;
#define LL long long
#define ULL unsigned long long
#define SZ(x) (int)x.size()
#define Lowbit(x) ((x) & (-x))
#define MP(a, b) make_pair(a, b)
#define MS(arr, num) memset(arr, num, sizeof(arr))
#define PB push_back
#define X first
#define Y second
#define ROP freopen("input.txt", "r", stdin);
#define MID(a, b) (a + ((b - a) >> 1))
#define LC rt << 1, l, mid
#define RC rt << 1|1, mid + 1, r
#define LRT rt << 1
#define RRT rt << 1|1
#define FOR(i, a, b) for (int i=(a); (i) < (b); (i)++)
#define FOOR(i, a, b) for (int i = (a); (i)<=(b); (i)++)
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const double eps = 1e-4;
const int MAXN = 300+10;
const int MOD = 10007;
//const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
const int seed = 131;
int cases = 0;
typedef pair<int, int> pii;

struct DIRECTION
{
    pii f, b, l, r;
    int db, dl, dr;
}dir[4];

char mp[500][500];

void init()
{
    //0上1下2左3右
    dir[0].f = {-1, 0}, dir[0].b = {1, 0}, dir[0].l = {0, -1}, dir[0].r = {0, 1};
    dir[1].f = {1, 0}, dir[1].b = {-1, 0}, dir[1].l = {0, 1}, dir[1].r = {0, -1};
    dir[2].f = {0, -1}, dir[2].b = {0, 1}, dir[2].l = {1, 0}, dir[2].r = {-1, 0};
    dir[3].f = {0, 1}, dir[3].b = {0, -1}, dir[3].l = {-1, 0}, dir[3].r = {1, 0};
    dir[0].db = 1, dir[0].dl = 2, dir[0].dr = 3;
    dir[1].db = 0, dir[1].dl = 3, dir[1].dr = 2;
    dir[2].db = 3, dir[2].dl = 1, dir[2].dr = 0;
    dir[3].db = 2, dir[3].dl = 0, dir[3].dr = 1;
}
string str;

void Solve()
{
    int x = 250, y = 250, cur = 3, ans = 0;
    int max_col = -1, max_row = -1, min_row = INF;
    for (int i = 0; i < SZ(str); i++)
    {
        max_col = max(max_col, y);
        max_row = max(max_row, x);
        min_row = min(min_row, x);
        assert(y >= 250);
        char c = str[i];
        if (c == 'F')
        {
            mp[x+dir[cur].f.X][y+dir[cur].f.Y] = '.';
            x += dir[cur].f.X, y += dir[cur].f.Y;
        }
        else if (c == 'B')
        {
            x += dir[cur].b.X, y += dir[cur].b.Y;
            mp[x][y] = '.';
            cur = dir[cur].db;
        }
        else if (c == 'L')
        {
            x += dir[cur].l.X, y += dir[cur].l.Y;
            mp[x][y] = '.';
            cur = dir[cur].dl;
        }
        else
        {
            x += dir[cur].r.X, y += dir[cur].r.Y;
            mp[x][y] = '.';
            cur = dir[cur].dr;
        }
    }
    for (int i = 250; i <= max_col+1; i++)
        for (int j = min_row-1; j <= max_row+1; j++)
            mp[j][i] = (mp[j][i] == '.' ? '.' : '#');
    printf("%d %d\n", max_row-min_row+3, max_col+2-250);
    for (int i = min_row-1; i <= max_row+1; i++) printf("%s\n", mp[i]+250);
}

int main()
{
    //ROP;
    init();
    int T;
    scanf("%d", &T);
    printf("%d\n", T);
    while (T--)
    {
        MS(mp, 0);
        cin >> str;
        Solve();
    }
    return 0;
}

K - Key to Knowledge (中途相遇法 + 状态压缩)

题意

给出n个01串,每个01串后面有个数字,代表正确的个数。问正确的答案有几种。如果只有一种输出。

思路

首先想到状态压缩,但是(1<<30)的规模显然要跪。

然后又想了一会儿中途相遇,想了一会儿,想不出相遇什么东西。

然后又YY了一些解法,还是想不出来。
然后就请教hcbbt巨巨。

然后他想到了枚举少的一方的状态然后暴力。我一听也觉得可以。然而我们都以为那时候的状态是 (1<<k) 了。

直到我敲完代码调试的时候才发现,那样状态是很多的( TДT)

然后就看了题解。

正解是用中途相遇法。

我们可以通过枚举前m/2位的正确答案的个数,然后得到这种情况下各串后面剩下的位需要多少个正确的答案,存在map里。

然后我们枚举剩下部分的正确答案个数。在map里找是不是有一模一样的。如果是的话,说明这部分的正确答案和以前那部分的正确答案是可以拼起来的,也就是完整的答案。

本来这个过程是O(nlogn)的,但是因为n比较小,可以直接按进制hash成一个数字,这样就变成了O(logn)。

学习了红名爷玄幻的代码。。。

代码

#include <stack>
#include <cstdio>
#include <list>
#include <cassert>
#include <set>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <functional>
#include <cstring>
#include <algorithm>
#include <cctype>
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <string>
#include <map>
#include <cmath>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
//using namespace __gnu_pbds;
#define LL long long
#define ULL unsigned long long
#define SZ(x) (int)x.size()
#define Lowbit(x) ((x) & (-x))
#define MP(a, b) make_pair(a, b)
#define MS(arr, num) memset(arr, num, sizeof(arr))
#define PB push_back
#define X first
#define Y second
#define ROP freopen("input.txt", "r", stdin);
#define MID(a, b) (a + ((b - a) >> 1))
#define LC rt << 1, l, mid
#define RC rt << 1|1, mid + 1, r
#define LRT rt << 1
#define RRT rt << 1|1
#define FOR(i, a, b) for (int i=(a); (i) < (b); (i)++)
#define FOOR(i, a, b) for (int i = (a); (i)<=(b); (i)++)
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const double eps = 1e-4;
const int MAXN = 3e5+10;
const int MOD = 10007;
const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
const int seed = 131;
int cases = 0;
typedef pair<int, int> pii;

struct POINT
{
    string str;
    int ans;
}p[20];
map<LL, int> mp;
LL hsh[1<<16];
int m, n;

void Solve()
{
    mp.clear();
    for (int sta = 0; sta < (1<<m/2); sta++)
    {
        LL x = 0;
        bool ok = true;
        for (int i = 0; i < n; i++)
        {
            int cur_ans = p[i].ans;
            for (int j = 0; j < m/2; j++)
                if ((p[i].str[j] == '1') == ((sta>>j)&1)) //如果出现了一个正确答案
                    cur_ans--;
            if (cur_ans < 0) ok = false;
            x = x * (m+1) + cur_ans;
        }
        if (!ok) x = -1;
        hsh[sta] = x;
        mp[x]++;
    }
    int ans_num = 0, ans = -1;
    for (int sta = 0; sta < (1<<(m-m/2)); sta++)
    {
        LL x = 0;
        for (int i = 0; i < n; i++)
        {
            int cur_ans = 0;
            for (int j = m/2; j < m; j++)
                if ((p[i].str[j] == '1') == ((sta>>(j-m/2)&1))) cur_ans++;
            x = x * (m+1) + cur_ans;
        }
        ans_num += mp[x];
        if (ans == -1 && mp[x] > 0)
        {
            for (int i = 0; i < (1<<(m/2)); i++)
                if (x == hsh[i])
                    ans = i + (sta<<(m/2));
        }
    }
    if (ans_num != 1) printf("%d solutions\n", ans_num);
    else
    {
        for (int i = 0; i < m; i++)
            printf("%d", (ans>>i)&1);
        puts("");
    }
}

int main()
{
    //ROP;
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n; i++) cin >> p[i].str >> p[i].ans;
        Solve();
    }
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值