Google Code Jam 2015 R2 C

题意:给出若干个句子,每个句子包含多个单词。确定第一句是英文,第二句是法文。后面的句子两者都有可能。两个语种会有重复单词。

现在要找出一种分配方法(给每个句子指定其文种),使得既是英文也是法文的单词数量最少。

分析:网络流的最小割。

建图方法如下,每个句子一个点。每个单词一个点。句子向其所属的单词连双向无穷流量边。把第一个句子作为起点,第二句作为终点。

现在我们要割掉一些单词,使得起点无法到达终点。

图的意义是这样的。如果我们能找到一条从起点到达终点的通路,那么中间一定有一个过程是从一个英文句子跳到一个单词,然后跳到一个法文句子。这就说明该单词既是英文又是法文。

而如果找不到通路,把所有能到达的句子点归为英文,其余的归为法文。这样就成功地完成了划分任务,而没有同属于两个语言的单词。

所以,割掉单词使得图没有通路就是一种划分的充要条件。

割点的方法就是拆点,每个单词拆成两点,一个负责入边,一个负责出边。中间加一条流量为1的边。

本题还有一个难点就是输入,每个句子要自己根据空格划分成单词。

stringstream可以将字符串作为输入流,从中读入内容。用stringstream sin(inputstring); 之后读入方法与cin一样。需要包含sstream头文件。

char*转化成string可以直接用等号赋值。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <string>
#include <sstream>
using namespace std;

#define D(x) 

const int MAX_N = 300;
const int MAX_DIC = 1010;
const int MAX_WORD_NUM = 20;

const int MAX_NODE_NUM = (MAX_N * 10 + 2000) * 2 + MAX_N;
const int MAX_EDGE_NUM = (int)(1e6);
#define INF 0x3f3f3f3f

struct Edge
{
    int next, v, f;
    Edge()
    {}
    Edge(int next, int v, int f):next(next), v(v), f(f)
    {}
} edge[MAX_EDGE_NUM * 2];

int head[MAX_NODE_NUM];
int q[MAX_NODE_NUM];
bool vis[MAX_NODE_NUM];
int cur[MAX_NODE_NUM];
int dep[MAX_NODE_NUM];
int edge_cnt;
int path[MAX_NODE_NUM];
int front, rear, q_size;

void add_edge(int u, int v, int f)
{
    edge[edge_cnt] = Edge(head[u], v, f);
    head[u] = edge_cnt++;
    edge[edge_cnt] = Edge(head[v], u, 0);
    head[v] = edge_cnt++;
}

void init()
{
    edge_cnt = 0;
    memset(head, -1, sizeof(head));
}

void q_init(int size)
{
    front = 0;
    rear = 0;
    q_size = size;
}

void q_push(int a)
{
    q[rear++] = a;
    rear %= q_size;
}

int q_pop()
{
    int ret = q[front++];
    front %= q_size;
    return ret;
}

void bfs(int s, int t)
{
    memset(vis, 0, sizeof(vis));
    memset(dep, -1, sizeof(dep));
    q_init(MAX_NODE_NUM);
    q_push(s);
    vis[s] = true;
    dep[s] = 0;
    while (front != rear && !vis[t])
    {
        int u = q_pop();
        for (int i = head[u]; ~i; i = edge[i].next)
        {
            int v = edge[i].v;
            if (!vis[v] && edge[i].f > 0)
            {
                q_push(v);
                vis[v] = true;
                dep[v] = dep[u] + 1;
            }
        }
    }
}

int add_flow(int path[], int &path_n)
{
    int min_edge = -1, delta = INF;
    for (int i = 0; i < path_n; ++i)
    {
        if (edge[path[i]].f < delta)
        {
            delta = edge[path[i]].f;
            min_edge = i;
        }
    }
    for (int i = 0; i < path_n; ++i)
    {
        edge[path[i]].f -= delta;
        edge[path[i] ^ 1].f += delta;
    }
    path_n = min_edge;
    return delta;
}

int last_node(int path[], int path_n, int s)
{
    if (path_n)
        return edge[path[path_n - 1]].v;
    return s;
}

int find_next(int start)
{
    for (int e = cur[start]; ~e; e = edge[e].next)
        if (edge[e].f && dep[start] + 1 == dep[edge[e].v])
            return e;
    return -1;
}

int dfs(int s, int t)
{
    int ret = 0;
    int path_n = 0;
    int x = s;
    memcpy(cur, head, sizeof(cur));
    while (true)
    {
        if (x == t)
        {
            ret += add_flow(path, path_n);
            x = last_node(path, path_n, s);
        }
        int next_edge = find_next(x);
        cur[x] = next_edge;
        if (next_edge == -1)
        {
            if (path_n == 0)
                break;
            dep[x] = -1;
            --path_n;
            x = last_node(path, path_n, s);
            continue;
        }
        path[path_n++] = next_edge;
        x = edge[next_edge].v;
    }
    return ret;
}

int dinic(int s, int t)
{
    int ret = 0;
    while (true)
    {
        bfs(s, t);
        if (dep[t] == -1)
            return ret;
        ret += dfs(s, t);
    }
    return -1;
}

int n;
map<string, int> dictionary;
vector<string> word[MAX_N];

void input()
{
    dictionary.clear();
    scanf("%d", &n);
    getchar();
    for (int i = 0; i < n; i++)
    {
        word[i].clear();
        char st[MAX_DIC * MAX_WORD_NUM];
        fgets(st, MAX_DIC * MAX_WORD_NUM, stdin);
        string s = st;
        stringstream sin(s);
        while (sin >> s)
        {
            if (dictionary.find(s) == dictionary.end())
                dictionary[s] = dictionary.size() - 1;
            word[i].push_back(s);
        }
    }
}

int work()
{
    init();
    for (int i = 0; i < (int)dictionary.size(); i++)
    {
        int id1 = i * 2 + n;
        int id2 = id1 + 1;
        add_edge(id1, id2, 1);
    }
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < (int)word[i].size(); j++)
        {
            int id = dictionary[word[i][j]];
            int id1 = id * 2 + n;
            int id2 = id1 + 1;
            add_edge(i, id1, INF);
            add_edge(id2, i, INF);
        }
    }
    return dinic(0, 1);
}

int main()
{
    int t;
    scanf("%d", &t);
    int case_num = 0;
    while (t--)
    {
        case_num++;
        printf("Case #%d: ", case_num);
        input();
        printf("%d\n", work());
        bfs(0, 1);
    }
    return 0;
}

时间: 2024-10-15 00:11:08

Google Code Jam 2015 R2 C的相关文章

Google Code Jam 2015 Round 1A Mushroom Monster 水

题意:每10秒扫描一下盘子,查看盘子里面的面包个数,问你主角用两种吃法可能吃得的最少的面包. 1)任意吃. 2)每秒以恒定速度. 解题思路:暴力,找差值. 解题代码: 1 // File Name: a.cpp 2 // Author: darkdream 3 // Created Time: 2015年04月18日 星期六 09时52分04秒 4 5 #include<vector> 6 #include<list> 7 #include<map> 8 #includ

Google Code Jam 2015 Round 1A Haircut 二分

题意:给你每个理发师的理发时间,问你排在队列中的第N个位置,问你应该被哪个理发师剪发. 解题思路:二分时间,看这个时间到第几个人理发了,然后找到临界值,看这个值的时候有那些理发师接待了新旅客的,即可找到那个理发师. 解题代码: 1 // File Name: b.sample.cpp 2 // Author: darkdream 3 // Created Time: 2015年04月18日 星期六 09时05分30秒 4 5 #include<vector> 6 #include<lis

Google Code Jam 2014 Round2

Google Code Jam Round2 晚上10点开始的,开始google一直上不去,然后开了个vpn就行了. 说说我的情况吧. P1就是某年noip普及组纪念品分组,贪心. p2是说给你一个排列,然后每次可以交换两个相邻的数,使得最后序列变为先上升后下降的样子.(n<=1000) 一开始题目看错了,以为是交换任意两个,然后愣了半天不会,去写后两题暴力了. (话说我Round1Cp1题目也看错了害的我逗了好久) 后来突然发现是adjacent elements...(<论英语学习的重要性

Google Code Jam 2009, Round 1C C. Bribe the Prisoners (记忆化dp)

Problem In a kingdom there are prison cells (numbered 1 to P) built to form a straight line segment. Cells number i and i+1 are adjacent, and prisoners in adjacent cells are called "neighbours." A wall with a window separates adjacent cells, and

Google Code Jam 2012 Practice - Store Credit

Problem You receive a credit C at a local store and would like to buy two items. You first walk through the store and create a list L of all available items. From this list you would like to buy two items that add up to the entire value of the credit

Google Code Jam Round 1A 2015 Problem B. Haircut 二分

Problem You are waiting in a long line to get a haircut at a trendy barber shop. The shop has B barbers on duty, and they are numbered 1 through B. It always takes the kth barber exactly Mk minutes to cut a customer's hair, and a barber can only cut

2015 Google code jam Qualification Round B 枚举 + 贪心

题意:给你一个无穷长的数列 和一些非 0 的值,可以进行两种操作. 1)数列中所有大于1的值 都减1 2)从 a[i] 中取出任意的值分给任意人. 问你最少执行多少步能使的 数列全为0. 解题思路:枚举最大的a[i].大于 a[i]的部分都分出去. 解题代码: 1 // File Name: b.cpp 2 // Author: darkdream 3 // Created Time: 2015年04月11日 星期六 23时16分58秒 4 5 #include<vector> 6 #incl

2015 Google code jam Qualification Round A 水

题意:给你一个序列 从 0-n  初始位置为0 ,只能从 i 走到 i+1  你必要有的人数 >= i+1  ,每个位置有a[i]个人,问你走到 n 还需要多少个人. 解题思路:暴力 解题代码: 1 // File Name: a.cpp 2 // Author: darkdream 3 // Created Time: 2015年04月11日 星期六 23时06分57秒 4 5 #include<vector> 6 #include<list> 7 #include<

google code jam Round 1A 2015 Problem C. Logging

Problem A certain forest consists of N trees, each of which is inhabited by a squirrel. The boundary of the forest is the convex polygon of smallest area which contains every tree, as if a giant rubber band had been stretched around the outside of th