https://atcoder.jp/contests/arc105/tasks
C - Camels and Bridge
题意:有一座桥,分为 m 部分,每部分长度为 l [ i ] l[i] l [ i ] ,最大承重为 v [ i ] v[i] v [ i ] ,有 n 个骆驼,每个重量为 w [ i ] w[i] w [ i ] ,所有骆驼从桥的一头开始按照某个顺序依次过桥,且相互距离保持不变,每部分桥上的重量不能超过这部分的最大承重,每部分桥两端点的重量不算。要安排过桥顺序和相互的距离,使得桥不塌,且首尾两个骆驼的距离最小,输出最小距离。
枚举+贪心
n 很小,可以枚举排列。所以现在需要在给定排列下,求最短长度。
贪心使得每个骆驼与它前一个的距离最小。
对于一只骆驼,它前面所有骆驼的距离都确定了,需要检查它与前面的所有骆驼,保证如果它们在一个部分,它们的重量之和不超过承重。但不好算距离。
所以考虑对一些骆驼,如果它们重量和为 sum,它们首尾最短需要保持的长度。这可以通过预处理桥的每一部分得到。
这样就可以枚举骆驼的区间,计算当前骆驼与前一只最小要的距离。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <bits/stdc++.h> #define debug(x) cout << #x << ":\t" << (x) << endl; using namespace std;#define ll long long const int N = 1e8 + 10 ;const int INF = 0x3f3f3f3f ;const ll inf = 0x3f3f3f3f3f3f3f3f ;int n, m;int w[100 ], l[100005 ], v[100005 ];int a[100 ], sum[100 ]; ll d[100 ];int lim[N], mn = INF;ll ans = inf; void cal () { for (int i = 1 ; i <= n; i++) { sum[i] = sum[i - 1 ] + w[a[i]]; } for (int i = 2 ; i <= n; i++) { ll di = lim[w[a[i]]]; for (int j = i - 1 ; j >= 1 ; j--) { di = max (di, lim[min (100000000 , sum[i] - sum[j - 1 ])] - d[i - 1 ] + d[j]); } d[i] = d[i - 1 ] + di; } ans = min (ans, d[n]); } int main () { scanf ("%d%d" , &n, &m); for (int i = 1 ; i <= n; i++) { scanf ("%d" , &w[i]); a[i] = i; } for (int i = 1 ; i <= m; i++) { scanf ("%d%d" , &l[i], &v[i]); lim[v[i] + 1 ] = max (lim[v[i] + 1 ], l[i]); mn = min (mn, v[i]); } for (int i = 1 ; i <= n; i++)if (w[i] > mn) { puts ("-1" ); return 0 ; } for (int i = 1 ; i < N; i++)lim[i] = max (lim[i - 1 ], lim[i]); do { cal (); } while (next_permutation (a + 1 , a + n + 1 )); if (ans >= inf)while (1 ); printf ("%lld\n" , ans); return 0 ; }
D - Let’s Play Nim
题意:n 个 背包,n 个盘子,两个人,背包里有金币,盘子为空。轮流操作,游戏分为两部分,第一部分:选一个背包以及一个盘子,把背包里的所有金币放到盘子里。当所有背包都空了,第二部分:选一个盘子拿走多于 0 的金币。不能拿的人输。
第二部分显然是一个nim游戏,所以要考虑第一部分的异或和。
如果 n 为奇数,则第二部分后手先拿,后手希望第一部分异或和不为0。
用如下策略:假设先手放入第一个盘子,后手每次都从剩下的包中选金币最多的,放入第一个盘子。
这样第一个盘子的金币必定多于一半,异或和必定不为 0。所以后手必胜。
如果 n 为偶数,则先手先拿。
如果相同含有相同数量金币的背包成对出现,后手总是能复制先手的操作,使得最终异或和为 0。这时后手必胜。
否则,先手用上面的策略,每次选最多的,放入第一个盘子,则超过一半,异或和必不为 0,这时先手必胜。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <bits/stdc++.h> #define debug(x) cout << #x << ":\t" << (x) << endl; using namespace std;#define ll long long const int N = 2e6 + 10 ;const int INF = 0x3f3f3f3f ;const ll inf = 0x3f3f3f3f3f3f3f3f ;int T;int n, a[N];int main () { scanf ("%d" , &T); while (T--) { scanf ("%d" , &n); for (int i = 1 ; i <= n; i++)scanf ("%d" , &a[i]); if (n & 1 ) { puts ("Second" ); continue ; } sort (a + 1 , a + n + 1 ); bool flg = 0 ; for (int i = 2 ; i <= n; i += 2 ) { if (a[i] != a[i - 1 ])flg = 1 ; } puts (flg ? "First" : "Second" ); } return 0 ; }