第十一届山东省大学生程序设计竞赛(正式赛)


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