GCJ 2018 Round 2

最重要的R2,然后那天状态好差,睡得很好,就是打不起精神来。然后,在第一个题上贪心贪了好久,想尽量去证明这个贪心策略,无果。第二题,水题迅速A掉。第三题,很裸的二分匹配,但是好久没碰图论了,大概知道怎么建图,就是时间来不及了。第四题,看CF上的讨论,大家都觉得比C还简单。。。。

很久没打比赛了,果然还是不行,做出第一和第三题就能拿到衣服。。。然后,再一想,不在第一题上纠结这么久的话,自己明明可以做出第二题和第三题的。。算了。。太弱了。。赶紧提升实力。。明年再来吧。。。(至少给我看到了希望,目标:学生时代,拿个GCJ的衣服。。

A

题意:

本场最好的一个题!
就是道尔顿实验,每个格子为左倾斜或者右倾斜或者空的板(最左端和最右端的格子必须为空),从每一列扔进去一个球,最后球会落到最底端。现在给定最底端,每列的球的总个数,让你构造高度最小的满足条件的各个网格的布局?

思路:

既然放在第一题的位置,肯定就是贪心题了,智障选手(我)只能猜猜结论,并不知道怎么去证明,靠感觉A贪心题。

首先:我们需要知道最底端的每个球来自于初始的哪个列?直觉告诉我们,尽量靠左放。
其次:如何构造格子的布局?维护一个每个球当前的位置,然后比较一下当前位置与目标位置的大小,进而构造格子的状态。
感兴趣的,可以去看看证明,个人觉得很好理解!

代码:

#include <bits/stdc++.h>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int>  P;
const int N = 105, MOD = 7+1e9;
int n, a[N], mp[N], loc[N];
int G[N][N];
bool check()
{
    for(int i = 1;i <= n;i ++)
        if(loc[i] != mp[i]) return false;
    return true;
}
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int T, ca = 0;
    cin >> T;
    while(T --)
    {
        cin >> n;
        for(int i = 1;i <= n;i ++) cin >> a[i];
        int cnt = 0;
        for(int i = 1;i <= n;i ++)
        {
            for(int j = 1;j <= a[i];j ++) 
            {
                mp[++cnt] = i;
            }
        }
        memset(G, 0, sizeof G);
        for(int i = 1;i <= n;i ++) loc[i] = i;
        int r = 0;
        bool flag = true;
        while(!check())
        {
            for(int i = 1;i <= n;i ++)
            {
                if(mp[i] > loc[i])
                {
                    if(loc[i] == 1 || loc[i] == n) 
                    {
                        flag = false;
                        break;
                    }
                    else G[r][loc[i]] = 1, loc[i] ++;
                }
                else if(mp[i] < loc[i]) 
                {
                    if(loc[i] == 1 || loc[i] == n)
                    {
                        flag = false;
                        break;
                    }
                    else G[r][loc[i]] = -1, loc[i] --;
                }
                else ;
            }
            r ++;
            if(!flag) break;
        }
        r ++;
        cout << "Case #" << (++ ca) << ": ";
        if(!flag)
        {
            cout << "IMPOSSIBLE" << endl;
            continue;
        } 
        else cout << r << endl;
        for(int i = 0;i < r;i ++)
            for(int j = 1;j <= n;j ++) 
            {
                if(G[i][j] == 1) cout << '\\';
                else if(G[i][j] == 0) cout << '.';
                else cout << '/';
                if(j == n) cout << '\n';
            }
    }
    return 0;
}

B

题意:

给定 r 个红球, b 个蓝球,可以使用数对 ( x , y ) 表示x个红球,y个蓝球凑成最终的 r 个红球, b 个蓝球,但是每个固定的数对 ( x , y ) 只能使用一次,问最多需要使用多少个不同的数对得到 ( r , b ) ,要求 x + y > 0
数据范围: r , b 500

思路:

一个简单的背包问题, d p [ i ] [ j ] 定义为凑出 i j 最多使用的不同的数对。
转移顺序比较重要,首先去从小到大枚举数对,然后转移。
h i n t :可以只枚举33以内的数对 ( x , y ) ,这样时间复杂度就是 O ( n 3 ) ,否则,你只能靠打表A这题了。

代码:

#include <bits/stdc++.h>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int>  P;
const int N = 510,MOD = 7+1e9;
int dp[N][N];
void init()
{
    int L = 33, r = 500, b = 500;
    memset(dp, -1, sizeof dp);
    dp[0][0] = 0;
    for(int i = 0;i <= L;i ++)
    {
        for(int j = 0;j <= L;j ++)
        {
            if(i + j <= 0) continue;
            for(int x = r;x - i >= 0;x --)
                for(int y = b;y - j >= 0;y --)
                {
                    if(dp[x-i][y-j] != -1)
                    {
                        dp[x][y] = max(dp[x][y], dp[x-i][y-j] + 1);
                    }

                }
        }
    }
}
int main()
{
    init();
    int T, ca = 0;
    cin >> T;
    while(T --)
    {
        int r, b;
        cin >> r >> b;
        cout << "Case #" << (++ ca) << ": " << dp[r][b] << endl;
    }
    return 0;
}

C

题意:

给定 N N 的网格,每个格子需要涂上一种颜色,现在有 2 N 种颜色,给出一种涂色方案,问要使每一行每一列都不能出现相同的颜色,最少需要改变多少个网格的颜色?
数据范围: N 100

思路:

也是经典的图论问题嘛,考虑每一种颜色,对网格上都涂这种颜色的点,建二分图,具体建图方式就是:X部位行号,Y部为列号,涂这种颜色的点坐标连边建图。然后求解二分图最大匹配,剩下的未匹配边,就是最少需要改变的网格个数。

代码:

#include <bits/stdc++.h>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int>  P;
const int N = 1e2 + 5,MOD = 7+1e9;
int n;
vector<P> G[2*N];
struct Edge
{
    int to, next;
}edge[N*N];
int head[N], tot;
void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u,int v)
{
    edge[tot].to = v; edge[tot].next = head[u];
    head[u] = tot++;
}
int linker[2*N];
bool used[2*N];
bool dfs(int u)
{
    for(int i = head[u]; i != -1 ;i = edge[i].next)
    {
        int v = edge[i].to;
        if(!used[v])
        {
            used[v] = true;
            if(linker[v] == -1 || dfs(linker[v]))
            {
                linker[v] = u;
                return true;
            }
        }
    }
    return false;
}
int hungary(int uN)
{
    int res = 0;
    memset(linker, -1, sizeof(linker));
    for(int u = 1;u <= uN;u ++)
    {
        memset(used, false, sizeof(used));
        if(dfs(u)) res ++;
    }
    return res;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
    freopen("in.txt","r",stdin);
#endif
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int T, ca = 0;
    cin >> T;
    while(T --)
    {
        cin >> n;
        for(int i = 1;i <= n;i ++)
            for(int j = 1;j <= n;j ++)
            {
                int x; cin >> x;
                G[n+x].PB(MP(i, j));
            }
        int res = 0;
        for(int i = 0;i <= 2*n;i ++)
        {
            int sz = G[i].size();
            if(sz == 0) continue;
            init();
            for(int x = 0;x < sz;x ++) addedge(G[i][x].FT, n + G[i][x].SD);
            res += hungary(n);
            G[i].clear();
        }
        cout << "Case #" << (++ ca) << ": " << (n * n - res) << endl;
    }
    return 0;
}

D

题意:

给定一个网格,每个格子会涂上黑色或者白色,现在每个格子连同其颜色扩充为 2 2 的方块,进行这样的操作无限次,问最后,在原图中的某个连通分量(同行或者同列则连边)在最后的拓展图中是否仍然存在(颜色布局必须完全相同)?

思路:

非常巧妙的构造思路,在原网格中,枚举坐标轴原点以及四个象限的颜色(必须和原点临接)然后统计即可。

猜你喜欢

转载自blog.csdn.net/u014686462/article/details/80379557