fork download
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3.  
  4. const int MOD = 1e9 + 7;
  5. int n, m;
  6. vector<int> adj[20];
  7. long long dp[1<<20][20];
  8. // dp[mask][u]: số cách đi qua tập mask, dừng tại u
  9.  
  10. int main(){
  11. cin.tie(0)->sync_with_stdio(0);
  12. // if(fopen("Hamiltonian_Flights.inp","r")){...}
  13.  
  14. cin >> n >> m;
  15. for(int i=0; i<m; i++){
  16. int u, v; cin >> u >> v;
  17. // Chuyển về 0-index: 1..n -> 0..n-1
  18. adj[u-1].push_back(v-1);
  19. }
  20.  
  21. // Cơ sở: Tại trạng thái chỉ có bit 0 (thành phố 1), đứng tại 0 -> có 1 cách
  22. dp[1][0] = 1;
  23.  
  24. // Duyệt qua tất cả các trạng thái từ mask nhỏ đến lớn
  25. for(int s = 2; s < (1 << n); s++){
  26. // Nếu mask s không chứa đỉnh xuất phát (bit 0) -> Bỏ qua
  27. if(!((s >> 0) & 1)) continue;
  28.  
  29. // Nếu mask s chứa đỉnh đích (bit n-1) nhưng chưa full đỉnh -> Bỏ qua
  30. // (Vì đến đích là dừng, không thể đi tiếp để lấp đầy mask được nữa)
  31. if(((s >> (n-1)) & 1) && s != ((1 << n) - 1)) continue;
  32.  
  33. // Duyệt các đỉnh i đang có trong mask s
  34. for(int i = 0; i < n; i++){
  35. if((s >> i) & 1){
  36. // Trạng thái trước đó: mask s bỏ đi bit i
  37. int prev = s ^ (1 << i);
  38.  
  39. // Ai có thể đi tới i?
  40. // Ở đây ta dùng tư duy ngược: Thay vì tìm ai đến i,
  41. // ta nên duyệt xuôi ở vòng for trước thì tốt hơn.
  42. // Nhưng để code ngắn gọn, ta duyệt các đỉnh j trong adj của prev
  43. // thì hơi khó vì adj là chiều xuôi.
  44. // -> Cách tối ưu: Duyệt j, xong đẩy cho adj[j]
  45. }
  46. }
  47. }
  48.  
  49. // Viết lại đoạn loop trên theo kiểu Push DP (Đẩy) cho thuận chiều adj
  50. // Reset lại loop cho dễ hiểu:
  51.  
  52. // Duyệt mask
  53. for(int s = 1; s < (1 << n); s++){
  54. // Chỉ xét mask có chứa đỉnh xuất phát
  55. if(!((s >> 0) & 1)) continue;
  56.  
  57. // Duyệt đỉnh u hiện tại đang đứng trong mask s
  58. for(int u = 0; u < n; u++){
  59. if((s >> u) & 1){
  60. if(dp[s][u] == 0) continue;
  61.  
  62. // Từ u bắn sang v
  63. for(int v : adj[u]){
  64. // Nếu v đã có trong mask s rồi -> Bỏ qua (đề bài thăm đúng 1 lần)
  65. if((s >> v) & 1) continue;
  66.  
  67. // Nếu v là đích (n-1) nhưng đi xong v mà mask chưa full -> Bỏ qua
  68. if(v == n-1 && (s | (1 << v)) != ((1 << n) - 1)) continue;
  69.  
  70. // Cộng dồn số cách
  71. int next_mask = s | (1 << v);
  72. dp[next_mask][v] = (dp[next_mask][v] + dp[s][u]) % MOD;
  73. }
  74. }
  75. }
  76. }
  77.  
  78. cout << dp[(1 << n) - 1][n-1];
  79. return 0;
  80. }
  81.  
Success #stdin #stdout 0s 5320KB
stdin
Standard input is empty
stdout
Standard output is empty