第十一屆山東省大學生程序設計競賽(正式賽)


B.Build Roads

題意:

無向圖,修公路,第i個城市的值是a[i],第j個城市的值是a[j],在他們之間修公路花費gcd(a[i] , a[j]),求修公路連接所有n個城市的最小花費

思路:(MST)

最小生成樹問題,對kruskal進行變形,但由於這題范圍太大,不能暴力枚舉所有任意兩個數的最大公約數,所以要考慮素數分布,然后我們再來看題面:n個點的最小生成樹,邊權是gcd(a[i] , a[j]),

當n很大時(差不多大於1000時),就肯定存在素數,邊權就都是1,得出的最小生成樹值就是n - 1。(結論直接用),在n小於1000時正常跑kruskal

另外還有一種情況,就是L = R時,說明邊權都是L,得出的最小生成樹就是(n - 1) * L,並且這里注意開long long

由於平常的kruskal算法都是直接給了說是有m條邊,但這個題沒給,所以不知道總邊數,那么可以開一個idx表示總邊數。

//本題示例
for
(int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { int w = gcd(a[i], a[j]); edge[idx ++ ] = {i , j , w}; }

 

  1 #include <iostream>
  2 #include <cstring>
  3 #include <string>
  4 #include <algorithm>
  5 #include <cmath>
  6 #include <queue>
  7 #include <vector>
  8 #include <map>
  9 #include <set>
 10 
 11 #define x first
 12 #define y second
 13 
 14 using namespace std;
 15 
 16 typedef long long LL;
 17 typedef pair<int, int>PII;
 18 
 19 const int N = 100010;
 20 
 21 int n, L, R, a[200001];
 22 int idx;
 23 int p[N];
 24 struct Edge
 25 {
 26     int a, b, w;
 27     bool operator < (const Edge& t) const
 28     {
 29         return w < t.w;
 30     }
 31 }edge[N];
 32 
 33 unsigned long long seed;
 34 unsigned long long xorshift64()
 35 {
 36     unsigned long long x = seed;
 37     x ^= x << 13;
 38     x ^= x >> 7;
 39     x ^= x << 17;
 40     return seed = x;
 41 }
 42 int gen() 
 43 {
 44     return xorshift64() % (R - L + 1) + L;
 45 }
 46 
 47 int gcd(int a, int b)
 48 {
 49     int c = a % b;
 50     while (c)
 51     {
 52         a = b;
 53         b = c;
 54         c = a % b;
 55     }
 56     return b;
 57 }
 58 
 59 int find(int x)
 60 {
 61     if (p[x] != x) p[x] = find(p[x]);
 62     return p[x];
 63 }
 64 
 65 LL kruskal()
 66 {
 67     sort(edge + 1, edge + 1 + idx);
 68     LL res = 0, cnt = 0;
 69 
 70     for (int i = 1; i <= idx; i++)
 71     {
 72         int a = edge[i].a, b = edge[i].b, w = edge[i].w;
 73 
 74         a = find(a), b = find(b);
 75         if (a != b)
 76         {
 77             p[a] = b;
 78             res += w;
 79             cnt++;
 80         }
 81         if (cnt == n - 1) break;
 82     }
 83     return res;
 84 }
 85 
 86 int main()
 87 {
 88     scanf("%d%d%d%llu", &n, &L, &R, &seed);
 89     for (int i = 1; i <= n; i++) a[i] = gen();
 90 
 91     if (L == R)
 92     {
 93         cout << (LL)(n - 1) * L << endl;//化為long long,不然有個點過不了
 94         return 0;
 95     }
 96     if (n >= 1000)
 97     {
 98         cout << n - 1 << endl;
 99         return 0;
100     }
101     //n < 1000     
102
103    for (int i = 1; i <= n; i++) p[i] = i; 104 105 for (int i = 1; i <= n; i++) 106 for (int j = 1; j <= n; j++) 107 { 108 int w = gcd(a[i], a[j]); 109 edge[idx ++ ] = {i , j , w}; 110 } 111 112 cout << kruskal() << endl; 113 114 return 0; 115 }

 

D.Dyson Box

題意:

二維空間里放了n個盒子,有水平往左和豎直往下兩種重力,求重力作用之后形成的輪廓周長。

思路:(模擬)(思維)

首先每次輸入一個方塊,周長就加4,然后判斷平移之后挨着別的方塊,這里拿向左平移舉例子,當輸入一個新的方塊后,當這一行原來有別的方塊時,平移之后肯定會左右挨着,這時就去掉兩條邊的

長度:

 

另外的情況就是當輸入一個新方塊后,向左平移之后上下挨着別的方塊:

 

因為上下挨着兩種情況對稱,考慮下面挨着的情況,那么上面挨着的情況就照葫蘆畫瓢就可以了,那么這種情況一定要想清楚,假如記這一行是x[xx],下面一行是x[xx - 1],判斷的是新輸入的這個方塊是否

有下面挨着的方塊,假如上面一行右邊那個方塊是新輸入的方塊,那么很顯然平移之后它下面沒有挨着的方塊,而假如上面一行左邊那個方塊是新輸入的方塊,那么它就有下面挨着的方塊,我們分析一下,

可以發現當下面一行的方塊數大於等於這一行的方塊數時,新輸入的這個方塊就有下面挨着的方塊,那么答案就去掉兩條邊的長度。

上面挨着的情況同理。

上面的圖的情況是新輸入的方塊沒有挨着的

 那么新輸入的方塊下面挨着別的方塊的情況再舉個例子:

 然后向下平移的情況就對着向左平移的情況照葫蘆畫瓢就完了

 

 1 #include <iostream>
 2 #include <cstring>
 3 #include <string>
 4 #include <algorithm>
 5 #include <cmath>
 6 #include <queue>
 7 #include <vector>
 8 #include <map>
 9 #include <set>
10 
11 #define x first
12 #define y second
13 
14 using namespace std;
15 
16 typedef long long LL;
17 typedef pair<int, int>PII;
18 
19 const int N = 200010;
20 
21 int n;
22 int x[N], y[N];
23 LL res1, res2;
24 
25 int main()
26 {
27     cin >> n;
28     for (int i = 1; i <= n; i++)
29     {
30         int xx, yy;
31         cin >> xx >> yy;
32         res1 += 4, res2 += 4;
33 
34         if (x[xx]) res1 -= 2;
35         x[xx] ++;
36         if (x[xx - 1] >= x[xx]) res1 -= 2;
37         if (x[xx + 1] >= x[xx]) res1 -= 2;
38         
39 
40         if (y[yy]) res2 -= 2;
41         y[yy] ++;
42         if (y[yy - 1] >= y[yy]) res2 -= 2;
43         if (y[yy + 1] >= y[yy]) res2 -= 2;
44         
45 
46         cout << res1 << ' ' << res2 << endl;
47     }
48 
49     return 0;
50 }

 

 

H.Adventurer's Guild

題意:

一個人去打怪獸,當他打過這個怪獸之后,會損失一定的生命值和疲勞值,並獲得一定數量的金幣,如果生命值減為零了,人就死了,而如果疲勞值減為零了,就相應的從生命值里扣,問保證這個人活着的前提下,能獲得的金幣的最大值是多少。

思路:(dp)

首先考慮結構體排序用貪心來做,但在分析的過程中我們就發現要考慮的條件實在是太多了,所以基本不太可能用貪心了。然后就考慮dp,發現dp是可以的,可以看出這個題是01背包的變形,即遇到一個怪獸,你是打還是不打,對應01背包中一個物品選還是不選的情況。

由於這個題是三維的,所以如果不優化的話只能過不到百分之四十的樣例,所以我們最后優化掉 i 這一維空間就可以了,注意關鍵點就是 j 這一維,就是生命值這一維,循環時是大於h[i]的(H到h[i]),

如果用集合角度考慮這個dp問題的話(閆氏dp分析法),即為這個人在生命值大於0時,可以獲得的最大金幣數。

最后注意開 long long

 1 #include <iostream>
 2 #include <cstring>
 3 #include <string>
 4 #include <algorithm>
 5 #include <cmath>
 6 #include <queue>
 7 #include <vector>
 8 #include <map>
 9 #include <set>
10 
11 #define x first
12 #define y second
13 
14 using namespace std;
15 
16 typedef long long LL;
17 typedef pair<int, int>PII;
18 
19 const int N = 1010;
20 
21 LL n, H, S;
22 LL h[N], s[N], w[N];
23 LL f[310][310];
24 
25 int main()
26 {
27     cin >> n >> H >> S;
28 
29     for (int i = 1; i <= n; i++) cin >> h[i] >> s[i] >> w[i];
30 
31     LL res = 0;
32     for (int i = 1; i <= n; i++)
33         for (int j = H; j > h[i]; j--)
34         {
35             for (int k = S; k >= 0; k--)
36             {
37                     if (k >= s[i]) f[j][k] = max(f[j][k], f[j - h[i]][k - s[i]] + w[i]);
38                     else
39                     {
40                         if (j - h[i] - (s[i] - k) > 0) 
41                             f[j][k] = max(f[j][k], f[j - h[i] - (s[i] - k)][0] + w[i]);
42 
43                     }
44                 
45                 res = max(res, f[j][k]);
46             }
47         }
48 
49     cout << res << endl;
50 
51     return 0;
52 }

M.Matrix Problem

題意:

給定 一個0/1矩陣C ,構造兩個矩陣 A , B ,其中 1 形成了完整的不分散的一塊四連通塊,並且對於 C 中所有位置,若是 1,則 A , B 對應位置必須都是 1,否則  A , B 之中必須有一個這個位置為 0 。 保證C陣的邊框都是0 。

思路:(思維)

這題真的讀懂題意是關鍵,因為這題說C的邊界都是0,所以我們可以讓A的最左邊一列都是 1 ,B的最右邊一列都是 1 ,然后行分奇偶,假如A的奇數行染1 , B的偶數行染1,那么這樣一定保證所有的1都是連通的。

注意的點就是存矩陣c的時候用char ,不要用int , 因為存的是某一個點的東西

 1 #include <iostream>
 2 #include <cstring>
 3 #include <string>
 4 #include <algorithm>
 5 #include <cmath>
 6 #include <queue>
 7 #include <vector>
 8 #include <map>
 9 #include <set>
10 
11 #define x first
12 #define y second
13 
14 using namespace std;
15 
16 typedef long long LL;
17 typedef pair<int, int>PII;
18 
19 const int N = 510;
20 
21 int n, m;
22 char c[N][N];
23 
24 int main()
25 {
26     cin >> n >> m;
27     for (int i = 1; i <= n; i++)
28         for (int j = 1; j <= m; j++)
29             cin >> c[i][j];
30 
31     for (int i = 1; i <= n; i++)//A矩陣
32     {
33         for (int j = 1; j <= m; j++)
34             if (i == 1 || (j % 2 == 1 && i != n)) cout << 1;
35             else cout << c[i][j];
36 
37         cout << endl;
38     }
39 
40     for (int i = 1; i <= n; i++)//B矩陣
41     {
42         for (int j = 1; j <= m; j++)
43             if (i == n || (j % 2 == 0 && i != 1)) cout << 1;
44             else cout << c[i][j];
45 
46         cout << endl;
47     }
48 
49     return 0;
50 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM