目录:
1.种类分配(Breed Assignment)
2.信息传递(Message Relay)
3.计算周长(Perimeter)
4.找奶牛(Find the Cow!)
1.种类分配(Breed Assignment)
题目:
农夫约翰有N只奶头,这N只奶牛分别属于三个种类:A,B,C。但是不幸的是,约翰忘记了每只奶牛分别属于哪个种类了。他仅仅只记得的K个奶牛之间的关系。例如,他记得奶牛1和奶牛2是同一种类,或者奶牛1和奶牛5是不同种类的。
问题描述:
给定这K个关系,请帮助约翰计算这N只奶牛可能的种类分布情况共有多少种。(当K个关系本身就是矛盾的时候,答案是0)。
输入:
第一行是两个正整数N和K,表示奶牛的数量和关系的数量。接下来K行,每行是一个奶牛种类的关系,“S x y”表示奶牛x和奶牛y是相同种类的,“D x y”表示奶牛x和奶牛y是不同种类的。
输出:
输出满足关系条件的奶牛种类分布情况共有多少种。
数据范围:
思路:
这道题其实就是普通的dfs,判断条件是否成立,然后当dfs完之后需要把没有条件的奶牛*3,就等于答案了——题目中说的自相矛盾其实可以不用理会,因为当这n个条件自相矛盾时,ans dfs的时候一定是0,0再乘3也还是0.
code:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<cmath>
using namespace std;
inline int read()
{
int x = 0,w = 0;
char ch = 0;
while(!isdigit(ch))
{
w |= ch == '-';
ch = getchar();
}
while(isdigit(ch))
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -x : x;
}
inline void write(int x)
{
if(x < 0)
putchar('-'),x = -x;
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
int tot,n,k,kkksc03;
int a[20],s[20];
char opt;
int x,y;
struct note
{
int a,b;
} q[60];
void dfs(int z)
{
if(z > n)
{
for(register int i = 1;i <= kkksc03;++i)
if(a[q[i].a] == a[q[i].b])
return ;
++tot;
return ;
}
if(s[z])
{
a[z] = a[s[z]];
dfs(z + 1);
return ;
}
a[z] = 1;
dfs(z + 1);
a[z] = 2;
dfs(z + 1);
a[z] = 3;
dfs(z + 1);
}
int main()
{
freopen("assign.in","r",stdin);
freopen("assign.out","w",stdout);
n = read();
k = read();
for(register int i = 1;i <= k;++i)
{
cin >> opt;
x = read();
y = read();
if(opt == 'S')
s[max(x,y)] = min(x,y);
else
{
q[++kkksc03].a = x;
q[kkksc03].b = y;
}
}
dfs(1);
printf("%d\n",tot);
}
2.信息传递(Message Relay)
题目:
农夫约翰的奶牛通常是按1到N进行编号的,奶牛们相互之间有一种特殊的信息传输方式。在信息传递的过程中,每只奶牛的信息最多传递到另一只奶牛,对于奶牛i,Fi表示他要传递信息的那只奶牛的编号,这里i和Fi肯定是不同的,如果Fi是0,则表示奶牛i没有要传递信息给其他的奶牛。
不幸的是,奶牛们知道了这种传递信息的方式可能会导致一个死循环。如果一个奶牛传递信息最终会导致一个死循环,那么我们就说这只奶牛在死循环里面。
问题描述:
请帮助奶牛们计算有多少头奶牛没有在死循环里面。
输入:
第一行一个正整数N,表示奶牛的数量。
接下来第2行到N+1行,每行一个非负整数,对于第i+1行上的数,表示奶牛i要把信息传递过去的奶牛的编号,如果是0则表示这头奶牛不需要传递信息。
输出:
输出不在死循环里面的奶牛的数量。
数据范围:
思路:
其实这道题就只要把他传递模拟一下即可,因为如果传递n次之后还没有传递0就可以证明一定进入死循环了,然后退出循环即可。
code:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<bitset>
#include<cstdio>
#include<cmath>
using namespace std;
inline int read()
{
int x = 0,w = 0;
char ch = 0;
while(!isdigit(ch))
{
w |= ch == '-';
ch = getchar();
}
while(isdigit(ch))
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -x : x;
}
inline void write(int x)
{
if(x < 0)
putchar('-'),x = -x;
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
int n,a[1010];
bitset<1010> vis,bz;
bool dfs(int z)
{
if(vis[z])
return 1;
vis[z] = 1;
if(a[z])
return dfs(a[z]);
return 0;
}
int main()
{
freopen("relay.in","r",stdin);
freopen("relay.out","w",stdout);
scanf("%d",&n);
for(register int i = 1;i <= n;++i)
scanf("%d",a + i);
for(register int i = 1;i <= n;++i)
{
if(bz[i])
continue;
vis.reset();
if(dfs(i))
bz |= vis;
}
printf("%d\n",n - bz.count());
}
3.计算周长(Perimeter)
题目:
农夫约翰在他的农田上了放置了N个干草堆,如果我们考虑农田是100*100的方格,每个干草堆占一个小方格(没有两个干草堆占据同一个小方格)。
约翰发现他的所有干草堆组成了一个连通分量,即从任意一个干草堆出发,都可以通过若干次向上或向下或向左或向右的移动到相邻的有干草堆的小方格而达到任意一个其他的干草堆。这里,干草堆的堆放可能会出现“洞”,“洞”是一块空地,但是都被干草堆所包围。
问题描述:
请帮助约翰计算所有被干草堆占领的小方格所组成的图形的周长。注意,“洞”是不计入周长的范围内的。
输入:
第一行一个正整数N,表示干草堆的个数。
接下来N行,每行两个整数,表示每个干草堆放置的位置。
输出:
输出周长。
数据范围:
说明:
注意“洞”是不计入周长的范围的。
思路:
这个问题可以通过模拟围绕包的旅行来解决。考虑问题中给出的例子(包括细胞的边界):
.-.-.
|X|X|
.-.-.-.-.
|X| |X|X|
.- - -.-.
|X|X|X|
.-.-.-.
我们只需要走在干草捆的外部边界上。走在边界外可以通过右手法则来完成:走路时把右手放在墙上。试着先向右转。如果不可用,则使用当前方向。如果当前方向也不可用,则向左拐。
S
|X X
v-<-<
|X X
在上面的图中,S是起始位置,方向是南。往南走一步,然后向右拐。但在下一步,我们不能右转,所以继续走一步。现在我们不能向右转或继续朝同一个方向走。所以我们必须在这个时候离开,等等。步行结束时,我们回到起点。首先,我们应该确定起始位置和方向。最左上角是一个合理的开始位置(在下面的图中用“s”表示),方向可以是南方向,也可以是东方向,假设我们选择了南。
S-<-<
|X X|
v ^-<-<
|X X X|
v >-^
|X X X|
>->->-^
为了实现这一思想,我们可以使用矩阵称为MAT。问题(1,1)是左下单元。实际上,没关系,我们可以使用矩阵(行,列)作为单元位置。在这种情况下,形状将被翻转,但周长将是相同的。在这个设置中,注意我们的步行坐标不同于矩阵单元的位置。我们认为(1,1)是细胞的左上角(1,1),其右下角是(2,2)。
(1,1).-.(1,2)
|X|
.-.
(2,1) (2,2)
因此,为了检查在行走时是否可以得到(y,x)的南向,垫[y] [x]必须包含包。同样地,MAT [Y-1] [X-1 ]必须被占据为北、垫[Y-1] [X]为东和垫[Y] [X-1 ]为西。
code:
# include<algorithm>
# include<iostream>
# include<cstring>
# include<cstdio>
# include<cctype>
# include<cmath>
using namespace std;
const int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int mat[102][102],perimeter,sy = 100,sx = 100,d;
inline int read()
{
int ret = 0,w = 0;
char ch = 0;
while(!isdigit(ch))
{
w |= ch == '-';
ch = getchar();
}
while(isdigit(ch))
{
ret = (ret << 3) + (ret << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -ret : ret;
}
inline void write(int ret)
{
if(ret < 0)
{
putchar('-');
ret = -ret;
}
if(ret > 9)
write(ret / 10);
putchar(ret % 10 + '0');
}
int main()
{
freopen("Perimeter.in","r",stdin);
freopen("Perimeter.out","w",stdout);
int n,x,y;
n = read();
for (int i=0; i<n; i++)
{
x = read();
y = read();
mat[y][x] = 1;
if (y < sy || (y == sy && x < sx))
sy = y,sx = x;
}
y = sy,x = sx;
do
{
y += dir[d][0],x += dir[d][1],perimeter++;
for (d = (d + 3) % 4;;d = (d + 1) % 4)
if ((d == 0 && mat[y][x]) || (d == 1 && mat[y - 1][x]) || (d == 2 && mat[y - 1][x - 1]) || (d == 3 && mat[y][x - 1]))
break;
}
while (y != sy || x != sx);
write(perimeter);
}
4.找奶牛(Find the Cow!)
题目:
奶牛贝里斯最近逃离了农夫约翰的农场躲在草丛里。于是,农夫约翰试图去找回他的奶牛。不幸的是,这些高高的草丛挡住了约翰的视线。现在,我们把这些草丛描述为一个长度为N的字符串,这个字符串只包含‘(’和‘)’这两种字符。例如,字符串:)((()())()) 。约翰知道贝里斯隐藏的前大腿很像连续的两个左括号((,隐藏的后大腿很像连续的两个右括号))。贝里斯的位置可以描述为:((的位置为X,))的位置为Y,并且X<Y。
问题描述:
请帮助约翰计算贝里斯隐藏的地方有多少种可能。
输入:
只有一行一个字符串。
输出:
输出贝里斯所有可能位置的总数。
数据范围:
思路:
直接暴力。(如果你觉得无聊也可以用KMP或AC自动机)
code:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
using namespace std;
string s;
int ans;
inline void write(int x)
{
if(x < 0)
putchar('-'),x = -x;
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
int main()
{
freopen("cowfind.in","r",stdin);
freopen("cowfind.out","w",stdout);
cin >> s;
int ret = 0;
for(int i = 0;i < s.length();i++)
{
if(s[i] == ')' && s[i + 1] == ')')
ans += ret;
else if(s[i] == '(' && s[i + 1] == '(')
ret++;
}
write(ans);
return 0;
}