NOIP模拟17.8.16
A 债务
文件名 输入文件 输出文件 时间限制 空间限制
debt.pas/c/cpp debt.in debt.out 1s 128MB
【题目描述】
小 G 有一群好朋友,他们经常互相借钱。假如说有三个好朋友 A,B,C。A
欠 B 20 元,B 欠 C 20 元,总债务规模为 20+20=40 元。小 G 是个追求简约的人,
他觉得这样的债务太繁杂了。他认为,上面的债务可以完全等价为 A 欠 C 20 元,
B 既不欠别人,别人也不欠他。这样总债务规模就压缩到了 20 元。
现在给定 n 个人和 m 条债务关系。小 G 想找到一种新的债务方案,使得每个
人欠钱的总数不变,或被欠钱的总数不变(但是对象可以发生变化),并且使得总
债务规模最小。
【输入格式】
输入文件第一行两个数字 n, m,含义如题目所述。
接下来 m 行,每行三个数字 ai
, bi, ci,表示 ai 欠 bi 的钱数为 ci。
注意,数据中关于某两个人 A 和 B 的债务信息可能出现多次,将其累加即可。
如”A 欠 B 20 元”、”A 欠 B 30 元”、”B 欠 A 10 元”,其等价为”A 欠 B 40 元”。
【输出格式】
输出文件共一行,输出最小的总债务规模。
【样例输入 1】
5 3
1 2 10
2 3 1
2 4 1
【样例输出 1】
10
【样例输入 2】
4 3
1 2 1
2 3 1
3 1 1
【样例输出 2】
0
【数据范围】
对于 30% 的数据,1 ≤ n ≤ 10,1 ≤ m ≤ 10。
对于 60% 的数据,1 ≤ n ≤ 100, 1 ≤ m ≤ 104。
对于 80% 的数据,1 ≤ n ≤ 104,1 ≤ m ≤ 104。
对于 100% 的数据,1 ≤ n ≤ 106,1 ≤ m ≤ 106。
对于所有的数据,保证 1 ≤ ai, bi ≤ n, 0 < ci ≤ 100。
【题解】
水题,略,注意细节,想清楚
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <iostream> 5 6 const int MAXN = 100 + 10; 7 8 inline void read(int &x) 9 { 10 x = 0;char ch = getchar(), c = ch; 11 while(ch < '0' || ch > '9')c = ch, ch = getchar(); 12 while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); 13 if(c == '-')x = -x; 14 } 15 16 char s[MAXN][MAXN];int n, ok; 17 18 int check(int x, int y) 19 { 20 if(s[x][y] == '.')return 0; 21 if(x == n || x == n - 1)return 1; 22 if(y == 1 || y == n)return 1; 23 if(s[x + 1][y] == '.' || s[x + 2][y] == '.' || s[x + 1][y - 1] == '.' || s[x + 1][y + 1] == '.')return 1; 24 s[x][y] = s[x + 1][y] = s[x + 2][y] = s[x + 1][y - 1] = s[x + 1][y + 1] = '.'; 25 return 0; 26 } 27 28 int main() 29 { 30 read(n); 31 for(register int i = 1;i <= n;++ i) scanf("%s", s[i] + 1); 32 for(register int i = 1;i <= n;++ i) 33 { 34 for(register int j = 1;j <= n;++ j) 35 if(check(i, j)) 36 { 37 ok = 1; 38 break; 39 } 40 if(ok)break; 41 } 42 if(ok)printf("NO"); 43 else printf("YES"); 44 return 0; 45 }
B 小 Z 搭积木
文件名 输入文件 输出文件 时间限制 空间限制
box.cpp box.in box.out 2s 128MB
【题目描述】
小 Z 喜欢搭积木。小 Z 一共有 n 块积木,并且积木只能竖着一块一块的摞,可
以摞多列。小 Z 的积木都是智能积木,第 i 块积木有一个情绪值 Xi。当摞在该积
木上面积木总数超过 Xi 时,i 号积木就会不高兴。小 Z 情商这么高,肯定不希望
有积木不高兴。但是他又希望每块积木都被用上,并且摞的积木列的总数最少。你
能帮帮萌萌的小 Z 吗?
【输入格式】
输入文件第一行一个数字 n,含义如题目所述。
第 2 行一共 n 个数,第 i 个数为 Xi,含义如题目所述。
【输出格式】
输出一个数字,表示最小的积木列数目。
【样例输入 1】
3
0 0 10
【样例输出 1】
2
4
【样例输入 2】
4
0 0 0 0
【样例输出 2】
4
【数据范围】
30% 数据,1 ≤ n ≤ 10
60% 数据,1 ≤ n ≤ 100
80% 数据,1 ≤ n ≤ 1000
100% 数据,1 ≤ n ≤ 5000
对于所有数据点,都有 Xi ≤ n
【题解】
我的方法是二分答案降序贪心放。
标解是这样的:
先将所有的盒子按照承载量从小到大排序。然后我们开一个数组,
记录一下当前一共有多少列,每一列一共有多少个盒子。从小到
大扫描所有的盒子,找到能放下的数量最多的列,放进去。如果没
有任何一列能放下,则建一个新列。 ——SD Ag爷 ty哥哥
#include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define min(a, b) ((a) < (b) ? (a) : (b)) const int MAXN = 5000 + 10; inline void read(int &x) { x = 0;char ch = getchar(), c = ch; while(ch < '0' || ch > '9')c = ch, ch = getchar(); while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); if(c == '-')x = -x; } int n, x[MAXN], ans, cnt[MAXN]; int check(int now) { memset(cnt, 0x3f, sizeof(cnt)); int p = 1, ok = 1; while(p <= n) { ok = 1; for(register int i = 1;i <= now;++ i) { if(!cnt[i])continue; cnt[i] = min(cnt[i] - 1, x[p]), ++p, ok = 0; } if(ok)return 0; } return 1; } int main() { read(n); for(register int i = 1;i <= n;++ i) read(x[i]); std::sort(x + 1, x + 1 + n, std::greater<int>()); register int l = 1, r = n, mid; while(l <= r) { mid = ((l + r) >> 1); if(check(mid))r = mid - 1; else l = mid + 1; } printf("%d", l); return 0; }
C 分宿舍
文件名 输入文件 输出文件 时间限制 空间限制
love.cpp love.in love.out 1s 128MB
【题目描述】
A 校有着神奇的住宿制度,不分男女宿舍,所有 n 个学生被统一分到两栋宿舍
楼中。作为年轻人,学生之间心生爱慕之情是很正常。我们用爱慕值来表示两名学
生之间的爱慕程度,如果两名爱慕值为 c 的学生被安排在同一宿舍楼,他们或她们
便会在一起,并造成影响力为 c 的早恋事件。
每年年末,身为政教处主任的你会将所有早恋事件按照影响力从大到小排成一
个列表,然后上报给校长。公务繁忙的校长只会去看列表中第一个事件的影响力,
如果影响很大,他会考虑撤换政教处主任。
在详细考察了 n 个学生之间的爱慕关系后,你觉得压力很大。你要合理的将学
生们分到两栋宿舍,以求产生的早恋事件影响力都比较小,以保住自己的官职。假
设只要处于同一栋宿舍楼的两个人之间有爱慕关系,他们就一定会在这年的某个时
候在一起。
那么,要怎么分配,才能让校长看到的那个早恋事件的影响力最小呢?这个最
小值是多少?
【输入格式】
第一行两个整数 n 和 m,分别表示学生的数目和爱慕关系的对数。
接下来 m 行,每行为 3 个正整数 ai,bi,ci,表示学生 ai 和 bi 之间有爱慕关
系,爱慕值为 ci。
数据保证 1 ≤ ai ≤ bi ≤ n,0 < ci ≤ 109,且每对爱慕关系只出现一次。
【输出格式】
输出一个数,为通过合理安排,校长看到的那个早恋事件的最小影响力。如果
没有发生早恋事件,输出 0。
6
【样例输入】
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
【样例输出】
3512
【数据范围】
对于 30% 的数据,n ≤ 15。
对于 70% 的数据,n ≤ 2000,m ≤ 50000。
对于 100% 的数据,n ≤ 20000,m ≤ 100000。
【题解】
并查集裸题。
1. 普通并查集
i表示第i个学生,i+n为虚拟节点,表示不能和i在一个宿舍的人
若两个点在同一并查集中,说明它们必须被分到同一个宿舍楼
将所有的爱慕关系从大到小排序
若a和b在同一并查集中,则此时c为答案
若不在同一并查集,令a与b+n所在并查集合并,b与a+n所在并查集合并
2. 加权并查集
同样将所有爱慕关系从大到小排序
每个点存储额外信息type,type为0表示和父亲结点在同一个宿舍楼,1表示和父亲结点不在同一个宿舍楼
合并与查询的方式类似食物链
3. 二分+dfs
二分答案
对于比二分答案大的爱慕关系,建图,显然若该图可以黑白染色,该答案可行;反之不可行
——gty
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 7 const int MAXN = 20000 + 10; 8 9 inline void read(int &x) 10 { 11 x = 0;char ch = getchar(), c = ch; 12 while(ch < '0' || ch > '9')c = ch, ch = getchar(); 13 while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); 14 if(c == '-')x = -x; 15 } 16 17 int n,m,fa[MAXN << 2],a[MAXN],b[MAXN],c[MAXN],cnt[MAXN]; 18 19 int find(int x) 20 { 21 return x == fa[x] ? x : fa[x] = find(fa[x]); 22 } 23 24 int cmp(int a, int b) 25 { 26 return c[a] > c[b]; 27 } 28 29 int main() 30 { 31 read(n), read(m); 32 register int tmp1, tmp2, tmp3, p1, p2, pp1, pp2; 33 for(register int i = (n << 1);i >= 1;-- i)fa[i] = i; 34 for(register int i = 1;i <= m;++ i)read(a[i]), read(b[i]), read(c[i]), cnt[i] = i; 35 std::sort(cnt + 1, cnt + 1 + m, cmp); 36 for(register int i = 1;i <= m;++ i) 37 { 38 tmp1 = a[cnt[i]], tmp2 = b[cnt[i]], tmp3 = c[cnt[i]]; 39 p1 = find(tmp1 << 1), p2 = find(tmp2 << 1); 40 pp1 = find(tmp1 << 1 | 1), pp2 = find(tmp2 << 1 | 1); 41 if(pp1 == pp2) 42 { 43 printf("%d", tmp3); 44 return 0; 45 } 46 fa[pp2] = p1; 47 fa[pp1] = p2; 48 } 49 printf("0"); 50 return 0; 51 }