題目描述:
有n片雪花,每片雪花由六個角組成,每個角都有長度。第i片雪花六個角的長度從某個角開始順時針依次記為ai,1,ai,2,…,ai,6。
因為雪花的形狀是封閉的環形,所以從任何乙個角開始順時針或逆時針往後記錄長度,得到的六元組都代表形狀相同的雪花。
例如ai,1,ai,2,…,ai,6和ai,2,ai,3,…,ai,6,ai,1就是形狀相同的雪花。ai,1,ai,2,…,ai,6和ai,6,ai,5,…,ai,1也是形狀相同的雪花。
我們稱兩片雪花形狀相同,當且僅當它們各自從某一角開始順時針或逆時針記錄長度,能得到兩個相同的六元組。求這n片雪花中是否存在兩片形狀相同的雪花。
輸入格式
第一行輸入乙個整數n,代表雪花的數量。接下來n行,每行描述一片雪花。每行包含6個整數,分別代表雪花的六個角的長度(這六個數即為從雪花的隨機乙個角順時針或逆時針記錄長度得到)。同行數值之間,用空格隔開。
輸出格式
如果不存在兩片形狀相同的雪花,則輸出:no two snowflakes are alike.
如果存在兩片形狀相同的雪花,則輸出:twin snowflakes found.
資料範圍
1≤n≤100000,
0≤ai,j<10000000
輸入樣例:
2
1 2 3 4 5 6
4 3 2 1 6 5
輸出樣例:
twin snowflakes found.
分析:
做這題前看別人說時間空間卡的很緊,所以遲遲沒有下手,結果用笨辦法一股腦刷刷寫完,竟然一遍ac了,也覺得很驚奇。
在介紹本題的解法之前,先引入字串的最小表示法的概念。我們需要求乙個字串的迴圈同構體中最小字典序的那個。比如bcda的迴圈同構體有bcda,cdab,dabc,abcd,其中最小表示法是abcd。
那麼如何高效的求解字串的最小表示法呢?顯然,暴力求解的話就是依次列舉字串的起點,再與其他位置作為起點的字串一一對比,總的時間複雜度是o(n ^2)。以abcdabcebdd為例詳細描述下暴力做法的過程。
定義雙指標i = 0,j = 1,首先a < b所以字串的第二個位置不會作為最優解字串的開頭,同理,後面的cd也都不會作為字串的開頭,直到j = 4匹配到了另乙個a,於是i,j均右移,發現後面的bc也都相等,繼續右移,直至遇見了d < e,所以abce不可能是最小表示法的字首,然後j又回到下標為5的位置,i也回到開頭,a < b,繼續比較。
上面的匹配過程可以採用類似於kmp演算法的思想來優化,一句話概括就是遇見不可能匹配的字首就快速跳過,不再進行比較。在比較到abcd和abce時,發現不匹配了,於是i指標還是回到起點,而j指標不必再回到abce中的b位置進行下一次匹配,因為一旦abce喪失了作為字串真字首的資格,其子串也都失去了作為字首的資格。比如說bce有作為字首的資格,則由之前的比較可知i指標指向的乙個字首bcd是比該字首小的,不論是abce的哪個子串,在abcd中都能夠找到字典序小於它的子串。所以:
abcdabcebdd
abcebddabcd
兩個迴圈同構體在d和e的位置失配時,第乙個指標重新回到下標為0的位置,而第二個指標不需要在從下標為0的位置的後乙個位置再進行比較,既然abce都失去了字首的資格,那麼第二個指標從第五個字元b再次開始比較即可,如此,指標便可快速右移。
總結下最小表示法的求解思路:
i,j分別指向待匹配兩個字串的首位置,k表示已匹配的長度,一旦當前的位置匹配,則k++,否則,字典序較大的那個字串的指標則要右移k + 1位才能再次進行比較。最小表示法**如下:
#include #include #include using namespace std;
int findmin(string s)
else
if (i == j) j++;
} return min(i, j);
}int main()
return 0;
}
說完了最小表示法,正式說下本題解題思路。本題需要判斷雪花是否相同,只要兩片雪花的六個角的最小表示法相同,我們就可以斷定這兩片雪花完全相同,所不同的是:上面介紹的最小表示法只能自左往右迴圈,而本題可以自右向左迴圈,只需讀入時備份一下逆置的陣列,然後僅保留兩個陣列的最小表示即可。
至於如何剪枝來避免不必要的比較,可以使用簡單的雜湊來解決。我們知道,兩片雪花相同的充要條件就是六個角最小表示法相同,如果對任意兩片雪花的六個角求和,則當兩片雪花的六角和不等時,他們一定不相同。考慮到本題的資料範圍,在求和對映時需要取模,一般取接近於最大範圍的最大的素數,我這裡取的是9999991。
綜上所述:本題的主要思路就是,先對所有雪花都雜湊對映到某個值,只有兩片雪花雜湊後的值相等時他們才可能相等,再比較他們的最小表示法即可驗證。
#include #include #include using namespace std;
typedef long long ll;
const int maxn = 100005;
int a[maxn][6],b[maxn][6],p[maxn];
unordered_map> m;
bool cmp(int *a,int *b)
return true;
}bool issame(int *a,int *b)
return true;
}void getmin(int *a)
else
if(i == j) j++;
}int s = min(i,j);
k = 0;
int b[6];
while(k < 6) b[k++] = a[(s++) % 6];
for(int i = 0;i < 6;i++) a[i] = b[i];
}void getunique(int *a,int *b)
}int gethash(int *a)
int main()
getunique(a[i],b[i]);
m[gethash(a[i])].push_back(i);
}for(auto x : m)}}
}}
cout<<"no two snowflakes are alike."
}
Acwing 137 雪花雪花雪花
有n片雪花,每片雪花由六個角組成,每個角都有長度。第i片雪花六個角的長度從某個角開始順時針依次記為ai,1,ai,2,ai,6ai,1,ai,2,ai,6。因為雪花的形狀是封閉的環形,所以從任何乙個角開始順時針或逆時針往後記錄長度,得到的六元組都代表形狀相同的雪花。例如ai,1,ai,2,ai,6a...
AcWing 137 雪花雪花雪花 初探hash
題目鏈結 本題的資料範圍顯然不能把所有陣列乙個乙個對比,可以建立乙個雜湊表,不同的h x 代表不同的雜湊值,雜湊值衝突的陣列就存在同乙個h x 開頭的煉表裡,然後根據雜湊值來對比對應的字串,減少重複比較。const int maxn 1e5 10 int a 10 snow maxn 6 h max...
演算法高階指南 Acwing 137 雪花雪花雪花
有n片雪花,每片雪花由六個角組成,每個角都有長度。第i片雪花六個角的長度從某個角開始順時針依次記為ai,1,ai,2,ai,6。因為雪花的形狀是封閉的環形,所以從任何乙個角開始順時針或逆時針往後記錄長度,得到的六元組都代表形狀相同的雪花。例如ai,1,ai,2,ai,6和ai,2,ai,3,ai,6...