Wednesday, September 12, 2012

USACO MAR11 Bovine Bridge Battle

Problem 8: Bovine Bridge Battle [Richard Ho, 2007]

Each of Farmer John's N (4 <= N <= 1,000) cows is patiently waiting
in the main pasture with cow i at point with integer coordinates
(X_i, Y_i) (-1,000,000,000 <= X_i <= 1,000,000,000; -1,000,000,000
<= Y_i <= 1,000,000,000).

The cows wish to form into groups of four in order to play Bridge,
their new favorite card game. Each group must satisfy an important
constraint: four cows are allowed to team up if and only if there
exists some point X somewhere in the plane (and not coincident with
any of the four points of the potential group of four) such that
rotating any of the group's cows 180 degrees about that point X
gives the position of some other cow in the group.

Please help the cows determine the number of sets of four cows that
can form a Bridge group.

By way of example, suppose eight cows are standing at eight points:

                  |
                 f*
                  |             a = (-3, 1)    e = (-1, 1)
           b*     |             b = (-2, 2)    f = ( 0, 3)
        a      e  |             c = (-3, 0)    g = ( 2, 0)
         *     *  |             d = (-2, 0)    h = ( 3, 0)
         c  d     |     g  h
---------*--*-----+-----*--*---------
                  |

Then the three legal sets of four cows are {a, b, e, d} (they rotate
around point (-2, 1)), {b, c, e, f} (around the point (-1.5, 1.5)),
and {c, d, g, h} (around (0,0)).

The supplied locations of the cows given are all distinct, although
they are supplied in no particular order. Furthermore, the answer
will fit into a signed 32-bit integer.

PROBLEM NAME: rotsym

INPUT FORMAT:

* Line 1: A single integer: N

* Lines 2..N+1: Line i+1 contains two space-separated integers: X_i
        and Y_i

SAMPLE INPUT (file rotsym.in):

8
-3 0
-2 0
-1 1
0 3
2 0
-3 1
3 0
-2 2

OUTPUT FORMAT:

* Line 1: A single integer that is the number of sets of 4 cows that
        form valid groups for bridge.

SAMPLE OUTPUT (file rotsym.out):

3


**********************************************************************

显然,直接4个for嵌套是绝对超时的。题目要求转180度后对称,那么,我们可以求出每两点之间连线的中点坐标,如果两个线段的中点坐标相等,那么它们上面的4点就满足条件。

由于计算中点坐标时需要用到除以2的操作,因此可能造成0.5出现。由于我们需要的不是中点坐标,只要中点坐标相等的线段,因此除以2可以直接忽略。

本人最初使用Hash表把点映射到正整数,然后用数组cnt[i]记录以标号i的点为中点的线段条数。那么答案就是Sigma{cnt[i] * (cnt[i] - 1) / 2}。不过Hash表消耗的内存较大,超过了16MB的限制。后来改用排序后扫描的办法,就顺利通过了。

06 
07 #include <stdio.h>
08 #include <stdlib.h>
09 #include <string.h>
10 #include <math.h>
11 
12 typedef struct
13 {
14     long x,y;
15 }POINT;
16 
17 int N;
18 POINT pos[1002];
19 POINT c[1002 * 1002 / 2];
20 
21 int pcmp(const void *a,const void *b)
22 {
23     POINT *m,*n;
24     m = (POINT *)a;n = (POINT *)b;
25     if(m->x > n->x)    return 1;
26     if(m->x == n->x && m->y > n->y)    return 1;
27     return -1;
28 }
29 
30 void solve()
31 {
32     long i,j;
33     long ans = 0;
34     long t = 0;
35     for(i = 0;i < N;i ++)
36         for(j = 0;j < i;j ++)
37         {
38             c[t].x = (pos[i].x + pos[j].x);
39             c[t].y = (pos[i].y + pos[j].y);
40             t ++;
41         }
42     //sort and then figure how many are the same
43     qsort(c,t,sizeof(POINT),pcmp);
44     long cnt = 1;
45     for(i = 1;i < t;i ++)   //be careful with cnt here
46     {
47         if(c[i].x == c[i - 1].x && c[i].y == c[i - 1].y)
48             cnt ++;
49         else
50         {
51             ans += cnt * (cnt - 1) / 2;
52             cnt = 1;
53         }
54     }
55     printf("%ld\n",ans);
56 }
57 
58 int main()
59 {
60     freopen("rotsym.in","r",stdin);
61     freopen("rotsym.out","w",stdout);
62     scanf("%d\n",&N);
63     int i;
64     for(i = 0;i < N;i ++)
65         scanf("%ld%ld\n",&pos[i].x,&pos[i].y);
66     solve();
67     fclose(stdout);
68     return 0;
69 }

USACO OPEN11 Learning Languages

Farmer John's N (2 <= N <= 10,000) cows, conveniently numbered 1..N,
are fluent in some M (1 <= M <= 30,000) languages, also conveniently
numbered from 1..M. Cow i can speak in K_i (1 <= K_i <= M) languages,
namely L_i1, L_i2,..., L_{iK_i} (1 <= L_ij <= M). FJ's cows aren't
THAT smart, so the sum of K_i over all cows i is at most 100,000.

Two cows can't directly talk to each other unless both speak a
common language. However, cows can pass messages along, translating
if necessary. In other words, cows A and B can have a conversation
if and only if there exists a sequence of cows T_1, T_2, ..., T_k
such that A and T_1 share a language, T_1 and T_2 share a language,
etc., and T_k and B share a language.

Farmer John wishes that his cows could be even more social, so he
wants all his cows to be able to socialize with any other cow. He
can buy books to teach any one of his cows any language he pleases.
Being a fairly frugal farmer, FJ wants to purchase the minimum
number of books necessary to enable all of his cows to speak to
each other. Help him determine:

    * The minimum number of books he must purchase

    * Any set of books assigned to cows in any order which will
      help him meet this goal; a program will grade your output.

By way of example, suppose there are three cows named Alberta,
Bessie, and Contessa along with three languages denoted as #1, #2,
and #3. Alberta can speak languages #2 and #3, Bessie can speak
language #2, and Contessa can speak language #1. Currently, Alberta
and Bessie can talk to each other, but Contessa is left alone.

                   #1   #2   #3
       Alberta           x    x
       Bessie            x
       Contessa     x

FJ wants to fix this situation, so he can buy Contessa a book to
teach her language #2. This will ensure all cows speak the same
language, so they can all communicate with one another.

Note that an alternate solution exists: instead, FJ could buy
Contessa a book to teach her language #3. Not all cows would speak
the same language, but this would still be a valid solution because
Contessa could communicate through Alberta (who also speaks language
#3) if she wants to talk to Bessie. Other alternatives exist, and any
valid alternate solution will also be accepted.

PROBLEM NAME: llang

INPUT FORMAT:

* Line 1: Two space-separated integers: N and M

* Lines 2..N+1: Line i+1 describes the languages that cow i can speak
        with (K_i)+1 space-separated integers: K_i, L_i1,
        L_i2,...,L_i{K_i}.

SAMPLE INPUT (file llang.in):

3 3
2 3 2
1 2
1 1

OUTPUT FORMAT:

* Line 1: A single integer that is the minimum number of books that FJ
        must purchase.

* Lines 2..B+1: Line i+1 contains two space-separated integers: the
        language id # and the id # of the cow to receive book i. If
        multiple solutions exist, print any one.

SAMPLE OUTPUT (file llang.out):

1
2 3

**********************************************************************
 
本人使用并查集解决这个问题。
先用M个链表保存会某个语言的牛的编号。然后把会相同语言的牛全部merge掉。最后
统计联通分量的个数ccnt。所需要的最少的书本数就是ccnt-1.
对于安排书本,本人是这样解决的。由上一步可知,不同联通分量之间一定不不存在
相同的语言。因此,任意选取一个被某个联通分量中的牛掌握的语言,然后把ccnt-1
本书分别安排到其他每个联通分量中。 
处理无向图联通性的问题经常使用到并查集。
 
01 #include <stdio.h>
02 #include <stdlib.h>
03 #include <string.h>
04 #define MAXN 100005
05
06 typedef struct node
07 {
08    struct node *next;
09    long v;
10 }node;
11
12 long N,M;
13 long fa[MAXN];   //并查集
14 node *head[30002] = {};   //链表
15 _Bool vis[MAXN] = {};    //辅助数组,判断某个联通分量是否被访问过
16
17 void add(long lang,long v)
18 {
19    node *p = malloc(sizeof(node));
20    p->v = v;
21    p->next = head[lang];
22    head[lang] = p;
23 }
24
25 long getfa(long x)
26 {
27    if(fa[x] != x)
28        fa[x] = getfa(fa[x]);
29    return fa[x];
30 }
31
32 void init()
33 {
34    scanf("%ld%ld\n",&N,&M);
35    long i,k,lang;
36    for(i = 1;i <= N;i ++)
37    {
38        scanf("%ld",&k);
39        while(k --)
40        {
41            scanf("%ld",&lang);
42            add(lang,i);
43        }
44    }
45 }
46
47 void solve()
48 {
49    long i,x,first;
50    long ccnt = 0;
51    node *p;
52    for(i = 1;i <= N;i ++)
53        fa[i] = i;
54    for(i = 1;i <= M;i ++)
55    {
56        if(head[i] != NULL)
57            x = getfa(head[i]->v);
58        for(p = head[i];p != NULL;p = p->next)
59            fa[getfa(p->v)] = x;
60    }
61    for(i = 1;i <= N;i ++)
62    {
63        x = getfa(i);
64        if(!vis[x])
65        {
66            vis[x] = 1;
67            ccnt ++;
68        }
69    }
70    printf("%ld\n",ccnt - 1);
71    for(x = 1;x <= M && head[x] == NULL;x ++);  //find the first lang known by at least one cow
72    first = getfa(head[x]->v);
73    memset(vis,0,sizeof(vis));
74    vis[first] = 1;
75    for(i = 1;i <= N;i ++)
76        if(!vis[getfa(i)])
77        {
78            vis[getfa(i)] = 1;
79            printf("%ld %ld\n",x,i);
80        }
81 }
82
83 int main()
84 {
85    freopen("llang.in","r",stdin);
86    freopen("llang.out","w",stdout);
87    init();
88    solve();
89    fclose(stdout);
90    return 0;
91 }

USACO OPEN11 Forgotten Password

Problem 8: Forgotten Password [Damon Doucet, 2011]

As has happened to so many of us, Bessie has forgotten her cowtube
password. She does, however, remember some handy bits of information
about it.

First, she remembers that her password (denoted as variable P) has
length L (1 <= L <= 1,000) roman characters and can be split into
one or more words (not necessarily unique) from a dictionary composed
of NW (1 <= NW <= 1,000) unique words.  A word W_i is defined as a
sequence of 1..20 lowercase letters of the Roman alphabet ('a'..'z').

She also remembers certain letters from her password along with
their positions.

Consider the following example. Bessie knows that her password looks
like "a??l?ban???????" ('?' represents a letter she cannot remember),
and her dictionary has the following words:

apple
cow
farmer
banana
bananas
pies

The two possible passwords for Bessie to have are "applebananapies" and 
"applebananascow".

Given the dictionary, and the letters that Bessie remembers, please
find her password. If more than one password is valid, find the
lexicographically least string that works.

PROBLEM NAME: forgot

INPUT FORMAT:

* Line 1: Two space-separated integers: L and NW

* Line 2: A string of length L: P

* Lines 3..NW+2: Line i+2 contains the ith word in the dictionary: W_i

SAMPLE INPUT (file forgot.in):

15 6
a??l?ban???????
apple
cow
farmer
banana
bananas
pies

OUTPUT FORMAT:

* Line 1: The lexicographically least password that fits

SAMPLE OUTPUT (file forgot.out):

applebananapies

**********************************************************************
 
这题有明显的无后效性很明显,使用动态规划解决。类似完全背包,用can[i]记录长度为i的串能否实现,
不断扩展,复杂度为(L*NW*20) 
只是题目要求输出排序后最小的那个,根据最优子结构性质,只要加上一个char str[i][MAXLEN],记录
长度为i时排序最靠前的那个串即可。
 
#include
#include
#include

int L,N;
char pass[1005];
char word[1005][25];
int len[1005];
int can[1005];
char str[1005][1005];
char tmp[1005];

void init()
{
   scanf("%d%d\n",&L,&N);
   scanf("%s\n",pass);
   int i;
   for(i = 0;i < N;i ++)
   {
       scanf("%s\n",word[i]);
       len[i] = strlen(word[i]);
   }
}

int match(char *a,char *b)
{
   for(;*a && *b;a ++,b ++)
   {
       if(*b == '?')    continue;
       if(*a != *b)    return 0;
   }
   return 1;
}

void dp()
{
   int i,j;
   can[0] = 1;
   for(i = 0;i <= L;i ++)
       if(can[i])
           for(j = 0;j < N;j ++)
               if(i + len[j] <= L && match(word[j],pass + i))
               {
                   if(can[i + len[j]] == 0)
                   {
                       can[i + len[j]] = 1;
                       memcpy(str[i + len[j]],str[i],i * sizeof(char));
                       memcpy(str[i + len[j]] + i,word[j],len[j] * sizeof(char));
                   }
                   else
                   {
                       strcpy(tmp,str[i]);
                       strcpy(tmp + i,word[j]);
                       if(strcmp(tmp,str[i + len[j]]) < 0)
                           memcpy(str[i + len[j]],tmp,1001 * sizeof(char));
                   }
               }
   puts(str[L]);
}

int main()
{
   freopen("forgot.in","r",stdin);
   freopen("forgot.out","w",stdout);
   init();
   dp();
   return 0;
}
起初本人程序中用得都是strcpy和strcat,在USACO上测试时超时了,换成memcpy就通过了。可见memcpy的效率大大高于strcpy。
 

USACO OPEN11 Corn Maze

Problem 6: Corn Maze [Sweet Tea Dorminy, 2010]

This past fall, Farmer John took the cows to visit a corn maze. But
this wasn't just any corn maze: it featured several gravity-powered
teleporter slides, which cause cows to teleport instantly from one
point in the maze to another. The slides work in both directions:
a cow can slide from the slide's start to the end instantly, or
from the end to the start. If a cow steps on a space that hosts
either end of a slide, she must use the slide.

The outside of the corn maze is entirely corn except for a single
exit.

The maze can be represented by an N x M (2 <= N <= 300; 2 <= M
<= 300) grid. Each grid element contains one of these items:

   * Corn (corn grid elements are impassable)

   * Grass (easy to pass through!)

   * A slide endpoint (which will transport a cow to the other
     endpoint)

   * The exit

A cow can only move from one space to the next if they are adjacent
and neither contains corn. Each grassy space has four potential
neighbors to which a cow can travel. It takes 1 unit of time to
move from a grassy space to an adjacent space; it takes 0 units of
time to move from one slide endpoint to the other.

Corn-filled spaces are denoted with an octothorpe (#). Grassy spaces
are denoted with a period (.). Pairs of slide endpoints are denoted
with the same uppercase letter (A-Z), and no two different slides
have endpoints denoted with the same letter. The exit is denoted
with the equals sign (=).

Bessie got lost. She knows where she is on the grid, and marked her
current grassy space with the 'at' symbol (@). What is the minimum
time she needs to move to the exit space?

Consider the following grid, with N=5 and M=6:

            ###=##
            #.W.##
            #.####
            #.@W##
            ######

A single slide has endpoints denoted by an uppercase W. Her optimal
strategy is to move right to the slide endpoint in 1 time, take the
slide in 0 time to the other endpoint, and move right and up in 2
more time to end on the exit.  This requires a total of 3 time, the
minimum.

PROBLEM NAME: cornmaze

INPUT FORMAT:

* Line 1: Two space separated integers: N and M

* Lines 2..N+1: Line i+1 describes row i of the maze: M characters (no
        spaces)

SAMPLE INPUT (file cornmaze.in):

5 6
###=##
#.W.##
#.####
#.@W##
######

OUTPUT FORMAT:

* Line 1: An integer, corresponding to the shortest time that Bessie
        needs to exit the maze.

SAMPLE OUTPUT (file cornmaze.out):

3


===================================================================

本题的关键在于构图,本人先对每个点标号,然后建立一张加权图。
还需要注意到的是,一旦到达某个传送点,就会立即转移到另一个出口。因此我把每个传送点拆成两个点,一个流入,另一个流出,并且在这一侧的流入和另一侧的流出点间建立一条边。
最后,SPFA计算从@点到=点的最短路即可。(最大点数有90000,朴素Dijstra不能承受)

001 #include
002 #include
003 #include
004 #define MAXN 302
005 #define INF 1000000000
006
007 int dx[4] = {0,0,-1,1};
008 int dy[4] = {1,-1,0,0};
009
010 typedef struct node
011 {
012    struct node *next;
013    short w;
014    long v;
015 }node;
016
017 node *first[MAXN * MAXN];
018
019 char map[MAXN][MAXN];
020 long in[MAXN][MAXN],out[MAXN][MAXN];
021 int M,N;
022 long dist[MAXN * MAXN];
023 long tot = 0;
024 long S,T;
025
026 inline void addedge(long a,long b,short c)
027 {
028    node *p = malloc(sizeof(node));
029    p->v = b;
030    p->w = c;
031    p->next = first[a];
032    first[a] = p;
033 }
034
035 void init()
036 {
037    int i,j,k;
038    int pos[28][2][2] = {};
039    int vis[28] = {};
040    scanf("%d%d\n",&N,&M);
041    for(i = 1;i <= N;i ++)
042    {
043        scanf("%s\n",&map[i][1]);
044        for(j = 1;j <= M;j ++)    //标号
045        {
046            if(map[i][j] == '#')    continue;
047            in[i][j] = out[i][j] = ++tot;
048            if(map[i][j] == '@')    S = out[i][j];
049            else if(map[i][j] == '=')    T = in[i][j];
050            else if(map[i][j] <= 'Z' && map[i][j] >= 'A')    //拆开传送点
051                out[i][j] = ++tot;
052        }
053    }
054    for(i = 1;i <= N;i ++)     //建图
055        for(j = 1;j <= M;j ++)
056        {
057            if(map[i][j] != '#')
058            {
059                for(k = 0;k < 4;k ++)
060                    if(map[i + dx[k]][j + dy[k]] != '#')    addedge(out[i][j],in[i + dx[k]][j + dy[k]],1);
061                if(map[i][j] <= 'Z' && map[i][j] >= 'A')
062                {
063                    if(!vis[map[i][j] - 'A'])
064                    {
065                        vis[map[i][j] - 'A'] = 1;
066                        pos[map[i][j] - 'A'][0][0] = i;
067                        pos[map[i][j] - 'A'][0][1] = j;
068                    }
069                    else
070                    {
071                        pos[map[i][j] - 'A'][1][0] = i;
072                        pos[map[i][j] - 'A'][1][1] = j;
073                    }
074                }
075            }
076        }
077    for(i = 0;i < 28;i ++)
078        if(vis[i])
079        {
080            addedge(in[pos[i][0][0]][pos[i][0][1]],out[pos[i][1][0]][pos[i][1][1]],0);
081            addedge(in[pos[i][1][0]][pos[i][1][1]],out[pos[i][0][0]][pos[i][0][1]],0);
082        }
083 }
084
085 long Q[MAXN * MAXN * 10],head,tail;
086
087 inline void enq(long x)
088 {
089    Q[tail ++] = x;
090 }
091
092 inline long deq()
093 {
094    return Q[head ++];
095 }
096
097 void SPFA()
098 {
099    long i;
100    node *p;
101    for(i = 1;i <= tot;i ++)
102        dist[i] = INF;
103    dist[S] = 0;
104    enq(S);
105    while(head < tail)
106    {
107        i = deq();
108        for(p = first[i];p != NULL;p = p->next)
109            if(dist[p->v] > dist[i] + p->w)
110            {
111                dist[p->v] = dist[i] + p->w;
112                enq(p->v);
113            }
114    }
115    printf("%ld\n",dist[T]);
116 }
117
118 int main()
119 {
120    freopen("cornmaze.in","r",stdin);
121    freopen("cornmaze.out","w",stdout);
122    init();
123    SPFA();
124    fclose(stdout);
125    return 0;
126 }