1. 程式人生 > >[jzoj]2018.08.09【NOIP提高組】模擬賽C組:解題報告

[jzoj]2018.08.09【NOIP提高組】模擬賽C組:解題報告

目錄:

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是不同種類的。

輸出:

輸出滿足關係條件的奶牛種類分佈情況共有多少種。

資料範圍:

1<=N<=15,1<=K<=50。

思路:

這道題其實就是普通的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則表示這頭奶牛不需要傳遞資訊。

輸出:

輸出不在死迴圈裡面的奶牛的數量。

資料範圍:

1<=N<=1000。

思路:

其實這道題就只要把他傳遞模擬一下即可,因為如果傳遞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行,每行兩個整數,表示每個乾草堆放置的位置。

輸出:

輸出周長。

資料範圍:

1<=N<=10000。

說明:

注意“洞”是不計入周長的範圍的。

思路:

這個問題可以通過模擬圍繞包的旅行來解決。考慮問題中給出的例子(包括細胞的邊界):
.-.-.
|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。

問題描述:

請幫助約翰計算貝里斯隱藏的地方有多少種可能。

輸入:

只有一行一個字串。

輸出:

輸出貝里斯所有可能位置的總數。

資料範圍:

1<=N<=50000。

思路:

直接暴力。(如果你覺得無聊也可以用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;
}