比赛-某暑假训练赛 Round1 (July, 2017)
A题:
网络流最大流模型。
用 [1, m] 表示各个国家,[m+1, m+n] 表示各个餐桌。
从 s 向 i 连边,容量为第 i 个国家的代表数;
从 m+i 向 t 连边,容量为第 i 张餐桌的容量;
从 i 向 j + m 连边,容量为 1 ,这样就限制了每个国家在一个餐桌上至多有一个代表。
找到最大流,表示满足条件的代表数量最大值,再与提前计算出的代表总数量比较,
两者如果相等说明存在合理方案,输出 1 ,否则输出 0.
B题:
二分图的最大匹配模型。
我在比赛的时候考虑了费用流,在“如何避免顾客合资买鞋”“如何有钱人买一大堆鞋”
等问题上纠结了很长的时间,还考虑过拆点等……这应该算是题目容易误导人的地
方吧。当然,费用流是不可取的。
将鞋和顾客分别作为二分图的一个点集,从每双鞋向可以购买它的顾客对应的点连边。
然后是一个贪心过程,将 n 双鞋按照价格降序排列,以排序后的点为起点,依次找增
广路,每次 i 号鞋成功找到增广路则 ans += price[i] ,最后输出 ans。
用二分图做这道题就满足了“一双鞋只对应一位顾客”这个条件。
C题:
二分图最大匹配模型。
注意到主对角线上的点满足任意两点行号列号均不同的性质。因此如果原图中有任意
两个点同行或同列,则不可能通过行列交换得到符合条件的图(行列交换后两点仍在
同行或同列)。问题简化为:判断是否有两点同行或同列。将黑色棋子行号作为二分
图的一个点集,列号作为另一个,每颗黑色棋子的行号对应点和列号对应点相连。同
一个行号对应点若有多条连边,表示该行有多个黑棋,列号对应点也是这样。执行二
分图最大匹配,若匹配数量等于 n ,表明没有两点同行或同列,输出 Yes ,否则 输
出 No.
D题:
最小割等于最大流。
建一个网络流模型。
从 s 向 i 连边,容量为第 i 个实验的赞助费;
从 i 向 m+j 连边,容量为 inf ,j 为 i 号实验对应的仪器编号;
从 j 向 t 连边,容量为 j 号仪器的费用。
1 <= i <= m; 1<= j <= n;
最小割的实际意义是选用仪器的费用和最小,找出最小割等于最大流 flow 。
提前计算赞助费用和 sum ,则 sum-flow 就是答案。
另外这道题给出实验对应仪器编号时是以换行符结尾的,不能直接读,我用了类似
输入优化的手写输入(getchar() 函数)来解决这个问题。
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 5 const int INF = 2100000000; 6 7 int s, t, ver, ans, sum, n, m; 8 int G[5005][5005], dis[5005], cnt[5005]; 9 10 int dfs(int node, int flow) 11 { 12 if (node == t) 13 return flow; 14 int p, tmp, dlt = 0; 15 for (p = 1; p <= ver; ++p) { 16 if (G[node][p] <= 0 || dis[p] + 1 != dis[node]) 17 continue; 18 tmp = dfs(p, std:: min(flow - dlt, G[node][p])); 19 G[node][p] -= tmp; 20 G[p][node] += tmp; 21 if ((dlt += tmp) == flow || dis[s] == ver) 22 return dlt; 23 } 24 if (!--cnt[dis[node]]) 25 dis[s] = ver; 26 ++cnt[++dis[node]]; 27 return dlt; 28 } 29 30 void solve() 31 { 32 int i, j; 33 ver = n + m + 2; 34 s = n + m + 1; 35 t = s + 1; 36 memset(G, 0, sizeof G); 37 memset(cnt, 0, sizeof cnt); 38 memset(dis, 0, sizeof dis); 39 ans = 0; 40 sum = 0; 41 42 for (i = 1; i <= m; ++i) { 43 scanf("%d", &G[s][i]); 44 sum += G[s][i]; 45 for (j = 1; j <= n; ++j) 46 G[i][m + j] = 1; 47 } 48 for (i = 1; i <= n; ++i) 49 scanf("%d", &G[m + i][t]); 50 while (dis[s] < ver) 51 ans += dfs(s, INF); 52 printf("%d\n", ans == sum); 53 } 54 55 int main() 56 { 57 scanf("%d%d", &m, &n); 58 while (m != 0 || n != 0) { 59 solve(); 60 scanf("%d%d", &m, &n); 61 } 62 return 0; 63 }
1 #include <stdio.h> 2 #include <algorithm> 3 4 using namespace std; 5 6 const int INF = 2100000000; 7 8 int s, t, n, m, ver, edge, id; 9 long long int ans; 10 int lnk[50005], bok[50005]; 11 int nxt[50005], fst[50005], v[50005]; 12 13 struct shoe { 14 int c; 15 int s; 16 } sho[50005]; 17 18 bool mycmp(shoe aaa, shoe bbb) 19 { 20 return aaa.c > bbb.c; 21 } 22 23 inline void insert(int x, int y) 24 { 25 v[++edge] = y; 26 nxt[edge] = fst[x]; 27 fst[x] = edge; 28 //printf("[%d->%d] ", x, y); 29 return ; 30 } 31 32 bool find(int node) 33 { 34 for (int p = fst[node]; p; p = nxt[p]) { 35 if (bok[v[p]] == id) 36 continue; 37 bok[v[p]] = id; 38 if (!lnk[v[p]] || find(lnk[v[p]])) { 39 lnk[v[p]] = node; 40 return true; 41 } 42 } 43 return false; 44 } 45 46 int main() 47 { 48 /* 1 ~ n shoes // n+1 ~ n+m persons */ 49 int i, j, price, size; 50 scanf("%d", &n); 51 for (i = 1; i <= n; ++i) 52 scanf("%d%d", &sho[i].c, &sho[i].s); 53 sort(sho + 1, sho + 1 + n, mycmp); 54 scanf("%d", &m); 55 for (i = 1; i <= m; ++i) { 56 scanf("%d%d", &price, &size); 57 for (j = 1; j <= n; ++j) 58 if ((sho[j].s == size || sho[j].s == size + 1) && price >= sho[j].c) 59 insert(j, n + i); 60 } 61 62 for (id = 1; id <= n; ++id) 63 if (find(id)) 64 ans += sho[id].c; 65 66 printf("%I64d\n", ans); 67 return 0; 68 }
1 #include <stdio.h> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 6 int n, ans, k, id; 7 int G[20005][20005], bok[500005], lnk[500005]; 8 9 int find(int node) 10 { 11 for (int p = 1; p <= n; ++p) { 12 if (!G[node][p] || bok[p] == id) 13 continue; 14 bok[p] = id; 15 if (!lnk[p] || find(lnk[p])) { 16 lnk[p] = node; 17 return true; 18 } 19 } 20 return false; 21 } 22 23 int main() 24 { 25 int i, i2, j2, t1; 26 scanf("%d", &k); 27 for (i = 1; i <= k; ++i) { 28 for (i2 = 1; i2 <= n; ++i2) { 29 for (j2 = 1; j2 <= n; ++j2) 30 G[i2][j2] = 0; 31 bok[i2] = 0; 32 lnk[i2] = 0; 33 } 34 35 ans = 0; 36 37 scanf("%d", &n); 38 for (i2 = 1; i2 <= n; ++i2) 39 for (j2 = 1; j2 <= n; ++j2) { 40 scanf("%d", &t1); 41 G[i2][j2] = t1; 42 } 43 for (id = 1; id <= n; ++id) 44 ans += find(id); 45 if (ans == n) 46 printf("Yes\n"); 47 else 48 printf("No\n"); 49 } 50 51 }
1 #include <stdio.h> 2 #include <algorithm> 3 using namespace std; 4 5 const int INF = 2100000000; 6 7 int n, m, s, t, edge, ver, ans, sum; 8 int fst[5005], nxt[5005], v[5005], w[5005], dis[5005]; 9 int cnt[5005]; 10 11 inline void insert(int x, int y, int z) 12 { 13 v[++edge] = y; 14 w[edge] = z; 15 nxt[edge] = fst[x]; 16 fst[x] = edge; 17 return ; 18 } 19 20 int dfs(int node, int flow) 21 { 22 if (node == t) 23 return flow; 24 int p, tmp, dlt = 0; 25 for (p = fst[node]; p; p = nxt[p]) { 26 if (w[p] <= 0 || dis[v[p]] + 1 != dis[node]) 27 continue; 28 tmp = dfs(v[p], std:: min(flow - dlt, w[p])); 29 w[p] -= tmp; 30 w[p + 1] += tmp; 31 if ((dlt += tmp) == flow || dis[s] == ver) 32 return dlt; 33 } 34 if (!--cnt[dis[node]]) 35 dis[s] = ver; 36 ++cnt[++dis[node]]; 37 return dlt; 38 } 39 40 int main() 41 { 42 int i, t1; 43 char tt; 44 scanf("%d%d", &m, &n); 45 ver = m + n + 2; 46 s = ver - 1; 47 t = ver; 48 for (i = 1; i <= m; ++i) { 49 scanf("%d", &t1); 50 insert(s, i, t1); 51 insert(i, s, 0); 52 sum += t1; 53 tt = getchar(); 54 while (tt != '\n') { 55 while (tt == ' ') 56 tt = getchar(); 57 t1 = tt - '0'; 58 while ((tt = getchar()) >= '0' && tt <= '9') 59 t1 = t1 * 10 + tt - '0'; 60 insert(i, t1 + m, INF); 61 insert(t1 + m, i, 0); 62 } 63 } 64 for (i = 1; i <= n; ++i) { 65 scanf("%d", &t1); 66 insert(m + i, t, t1); 67 insert(t, m + i, 0); 68 } 69 while (dis[s] < ver) 70 ans += dfs(s, INF); 71 printf("%d\n", sum - ans); 72 return 0; 73 }
A餐桌安排问题 | ||
|
问题描述
有来自 m个国家的代表参加一个国际会议。其中第i号国家的代表数为Ri,。会议餐厅共有 n张餐桌,其中第i餐桌可容纳 Ci 个人就餐。为了使代表们充分交流, 主办方希望找到一种安排就餐的方法,使得同一个国家的代表不在同一个餐桌就餐。
问是否存在这样的餐桌安排方案,有输出1否则输出0。
输入格式
有若干组测试数据,对于每组数据:
第1行有 2 个正整数m和 n,m表示国家数,n表示餐桌数。
第2行有m个正整数,分别表示每个国家的代表数。
第3行有n个正整数,分别表示每个餐桌的容量。
输入以一行,两个空格间隔的0作为结束。
输出格式
对于每组数据,输出一行,若有解输出1,否则输出0
样例输入
4 5
4 5 3 5
3 5 2 6 4
7 15
12 10 9 10 9 1 12
20 8 15 1 14 13 12 14 6 18 7 2 16 17 2
11 18
10 18 9 5 6 3 14 10 8 14 18
12 1 2 3 14 15 18 7 20 13 5 8 18 18 20 2 11 10
0 0
样例输出
1
1
0
提示
对于100%的数据:1<=m<=150 1<=n<=300 数据组数不超过10
B鞋店 | ||
|
问题描述
何老板经营的鞋店有n双鞋。每双鞋都有一定价格和尺码,对于第i双鞋,Ci表示它的价格,Si表示它的尺码。每双鞋的尺码都不相同。
店里来了m个客户,每个客户都想买一双鞋,其中第i个客户有Di块钱,他的脚的尺码是Li。每个客户都愿意买尺码跟他脚相同或者大一码的鞋子。也就是对于j号鞋子,若满足(Cj<=Di)&&(Li==Sj||Li==Sj-1),i号客户就愿意买j号鞋。
贪婪的何老板希望卖出去的鞋子的总价尽可能高,请你帮他算算,他最多能卖出多少钱?
注意:一个顾客最多买一双鞋,一双鞋最多卖给一个顾客
输入格式
第一行,一个整数n
接下来n行,每行两个整数Ci和Si,表示一双鞋子的价格和尺码
接下来一行,一个整数m
接下来m行,每行两个整数Di和Li,表示一个客户的钱和他的脚的尺码
输出格式
一行,一个整数,表示能卖出去的最大总额。
样例输入 1
3
10 1
30 2
20 3
2
20 1
20 2
样例输出 1
30
样例输入 2
3
10 4
20 5
30 6
2
70 4
50 5
样例输出 2
50
提示
对于30%的数据1<=n,m<=100
对于100%的数据:
1<=n<=5000
1<=m<=10000
1<=Ci,Si,Di,Li<=1000000000
C行列交换 | ||
|
问题描述
有一个n*n的方格矩阵。上面摆放有一些黑色棋子。每次操作,你可以选择其中任意两行,交换这两行,也可以选择其中的任意两列,交换这两列。
问,若干次操作后,能否使得使得矩阵的主对角线上全是黑色棋子。(左上角到右下角的对角线为主对角线)。能输出"Yes",否则输出"No"。
输入格式
第一行一个整数K,表示有K组测试数据。
对于每组数据:
第一行一个整数n,表示矩阵的尺寸
接下一个有数字0和1构成的n*n的矩阵(其中1表示黑色棋子)。
输出格式
K行,每行对应一个组数据的结果,有解输出Yes,无解输出No
样例输入
2
3
0 0 1
0 1 0
1 0 0
2
0 0
0 1
样例输出
Yes
No
提示
对于100%的数据:K≤20,N ≤ 200
D太空飞行计划 | |
|
问题描述
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej需要用到的仪器是I的子集Rj属于I。配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。
W教授的任务是找出一个有效算法,确定在一次太飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
输出最多能得到的净获利。
输入格式
第1行有2 个正整数m和n。m是实验数,n是仪器数。(n,m<=50)
接下来的m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。
最后一行的n个数是配置每个仪器的费用。
输出格式
一行,包含一个整数,表示最大净收益
样例输入 1
2 3
10 1 2
25 2 3
5 6 7
样例输出 1
17
样例输入 2
6 22
9 2 7
4 5 6 7 9
3 2
10 2
10 3 6 11
8 4 5 6 9
6 1 3 7 10 2 5 8 3 5 7 6 10 6 6 2 4 6 5 5 3 9
样例输出 2
16
提示
样例1说明,做了1,2两个实验,用到的仪器是1,2,3
样例2说明,做了1,3,4三个实验,用到的仪器是2和7