洛谷 P2921 在農場萬聖節Trick or Treat on the Farm題解

題意翻譯

題目描述

每一年,在威斯康星州,奶牛們都會穿上衣服,收集農夫約翰在N(1<=N<=100,000)個牛棚隔間中留下的糖果,以此來慶祝美國秋天的萬聖節。html

因爲牛棚不太大,FJ經過指定奶牛必須遵循的穿越路線來確保奶牛的樂趣。爲了實現這個讓奶牛在牛棚裏來回穿梭的方案,FJ在第i號隔間上張貼了一個「下一個隔間」Next_i(1<=Next_i<=N),告訴奶牛要去的下一個隔間;這樣,爲了收集它們的糖果,奶牛就會在牛棚裏來回穿梭了。ios

FJ命令奶牛i應該從i號隔間開始收集糖果。若是一隻奶牛回到某一個她已經去過的隔間,她就會中止收集糖果。數組

在被迫中止收集糖果以前,計算一下每頭奶牛要前往的隔間數(包含起點)。oop

輸入格式

第1行 整數n。post

第2行到n+1行 每行包含一個整數 next_i 。this

輸出格式

n行,第i行包含一個整數,表示第i只奶牛要前往的隔間數。spa

樣例解釋

有4個隔間翻譯

隔間1要求牛到隔間13d

隔間2要求牛到隔間3code

隔間3要求牛到隔間2

隔間4要求牛到隔間3

牛1,從1號隔間出發,總共訪問1個隔間;

牛2,從2號隔間出發,而後到三號隔間,而後到2號隔間,終止,總共訪問2個隔間;

牛3,從3號隔間出發,而後到2號隔間,而後到3號隔間,終止,總共訪問2個隔間;

牛4,從4號隔間出發,而後到3號隔間,而後到2號隔間,而後到3號隔間,終止,總共訪問3個隔間。

翻譯提供者:吃葡萄吐糖

題目描述

Every year in Wisconsin the cows celebrate the USA autumn holiday of Halloween by dressing up in costumes and collecting candy that Farmer John leaves in the N (1 <= N <= 100,000) stalls conveniently numbered 1..N.

Because the barn is not so large, FJ makes sure the cows extend their fun by specifying a traversal route the cows must follow. To implement this scheme for traveling back and forth through the barn, FJ has posted a 'next stall number' next_i (1 <= next_i <= N) on stall i that tells the cows which stall to visit next; the cows thus might travel the length of the barn many times in order to collect their candy.

FJ mandates that cow i should start collecting candy at stall i. A cow stops her candy collection if she arrives back at any stall she has already visited.

Calculate the number of unique stalls each cow visits before being forced to stop her candy collection.

POINTS: 100

輸入格式

* Line 1: A single integer: N

* Lines 2..N+1: Line i+1 contains a single integer: next_i

輸出格式

* Lines 1..N: Line i contains a single integer that is the total number of unique stalls visited by cow i before she returns to a stall she has previously visited.

輸入輸出樣例

輸入 #1
4 
1 
3 
2 
3 
輸出 #1
1 
2 
2 
3 

說明/提示

Four stalls.

* Stall 1 directs the cow back to stall 1.

* Stall 2 directs the cow to stall 3

* Stall 3 directs the cow to stall 2

* Stall 4 directs the cow to stall 3

Cow 1: Start at 1, next is 1. Total stalls visited: 1.

Cow 2: Start at 2, next is 3, next is 2. Total stalls visited: 2. Cow 3: Start at 3, next is 2, next is 3. Total stalls visited: 2. Cow 4: Start at 4, next is 3, next is 2, next is 3. Total stalls visited: 3.


 

題解

這個題雖然是圖的題目,可是它的圖很特殊,每一個節點的出度都是1,並且必定存在環。

直接暴力能夠得40分。

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <string.h>

using namespace std;

const int MAXN = 1e5 + 5; 
int n, next[MAXN], vis[MAXN], ans[MAXN], p; 

int main()
{
 cin >> n;
 for(int i = 1; i <= n; i++)
 {
  cin >> next[i];
 }
 for(int i = 1; i <= n; i++)
 {
  memset(vis, 0, sizeof(vis));
  ans[i] = 1;
  p = i;
  vis[p] = 1;
  while(vis[next[p]] == 0)
  {
   vis[next[p]] = 1;
   p = next[p];
   ans[i]++;
  }
 }
 for(int i = 1; i <= n; i++)
 {
  cout << ans[i] << endl;
 }
 
 return 0;
}

剩下的問題就是如何解決TLE的問題。和01迷宮問題的思路相似,須要對圖進行染色,找到環,同一個環上的各個節點的隔間數是相同的,把這個結果保存下來就能夠減小後續的運算量了。下圖是樣例染色的結果。1號節點是個自環長度爲1,2和3號節點是長度爲2的環,因此這兩個節點輸出的隔間數都是環的長度2。4號節點通過1步走到2和3組成的環,因此它的隔間數是1+2=3。

 染色的過程有兩種可能:

1)染色的過程遇到了同種顏色的節點,例以下圖中2-3-4-3。

2)染色的過程當中遇到了不一樣顏色的節點,例以下圖中的5-2和6-4。

爲了方便計算環的長度和走過的隔間數,咱們引入兩個變量:cnt和dfn數組。cnt從0開始,用於計算這次染色所通過的隔間數,每走一次加1。dfn記錄每一個節點和這次染色第一個節點之間的距離。例如第一種染色狀況,dfn(2)=0,dfn(3)=1,dfn(4)=2。當從4走到3時,咱們馬上能夠發現一個同色節點組成的環。環的長度是cnt-dfn(3)=2。並且咱們能夠記錄下這個顏色路徑進入環的第一個節點是3號節點。

對於第2種染色過程,又分爲兩種狀況:

1)所遇到的不一樣顏色的節點在環上,如4號節點。這種狀況隔間數等於cnt+環長。

2)所遇到的不一樣顏色的節點不在環上,如2號節點。這種狀況隔間數等於cnt+環長+從所遇到的節點到第一個進入環的節點的距離。

而判斷節點是否在環上,能夠用dfn的大小進行比較,全部比第一個進入環的節點的dfn小的節點都在環外,反之在環上。

下面就是改進後的代碼:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <math.h>
 4 #include <algorithm>
 5 #include <string.h>
 6 
 7 using namespace std;
 8 
 9 const int MAXN = 1e5 + 5; 
10 int n, next[MAXN], vis[MAXN], ans[MAXN], p; 
11 int dfn[MAXN], cnt;
12 int looplen[MAXN]; // 環長 
13 int stloop[MAXN]; // 路徑中進入環的第一個節點的dfn值 
14 
15 int main()
16 {
17     cin >> n;
18     for(int i = 1; i <= n; i++)
19     {
20         cin >> next[i];
21     }
22 
23     memset(vis, 0, sizeof(vis));
24     for(int i = 1; i <= n; i++)
25     {
26         cnt = 0;
27         p = i;
28         while(vis[p] == 0)
29         {
30             vis[p] = i;
31             dfn[p] = cnt;
32             p = next[p];
33             cnt++;
34         }
35         if(vis[p] == i)
36         { // 走到了本身染色的環 
37             ans[i] = cnt;
38             looplen[i] = cnt - dfn[p];
39             stloop[i] = dfn[p];
40          } 
41          else
42          { // 走到了其餘點染色的路徑
43              looplen[i] = looplen[vis[p]];
44              stloop[i] = cnt;
45              if(stloop[vis[p]] > dfn[p])
46             { // 遇到的點不在環上 
47                  stloop[i] += stloop[vis[p]] - dfn[p];
48             }          
49              ans[i] = stloop[i] + looplen[i];
50          }
51     }
52     for(int i = 1; i <= n; i++)
53     {
54         cout << ans[i] << endl;
55     }
56     
57     return 0;
58 }
相關文章
相關標籤/搜索