AOJ-722 发红包
Description
过年了,大家都热衷于在支付宝和微信上面送给亲朋好友红包,西瓜也很喜欢参加这个活动,不仅如此,他还想成为他和他的好友中发红包金额最多的人以表示他的土豪。所以西瓜想通过某些方式来推测是否有这种可能。
虽然支付宝提供了“我的红包榜”功能可以查看自己和朋友们的到底发出和接收了多少红包,但是微信并没有提供类似查询朋友发出和收到了多少红包金额的功能。
西瓜为了知道自己是不是最土豪的,只好使用了自己的专业技能。西瓜编写了一个黑客小程序,利用这个小程序西瓜知道了在微信上他的好友相互之间的流通金额(双方发出红包金额的总和,比如A给B 6元红包,B给A 4元红包,这样的话A和B之间的流通金额是10元)。现在他想通过这些信息来得知,他自己是否有可能成为他好友之中发出红包金额最多的人(即在微信上发出的红包金额和在支付宝上发出的红包金额最多的人)。
不过他并不知道怎么做,所以他找到了聪明的你,让你来帮他计算是否有这种可能性。
他给你的信息有这些:
1、在支付宝中所有人的支出金额
2、在微信中所有人相互之间的流通金额
现在他想请你帮忙,让你通过他给你的这些信息来判断是否有这样的可能,使得西瓜能成为他好友之中支出金额最大的人。
所有金额的最低单位为分,所有输入均精确到两位小数。并且西瓜期望的是他的支出金额必须严格大于他的好友们。
虽然支付宝提供了“我的红包榜”功能可以查看自己和朋友们的到底发出和接收了多少红包,但是微信并没有提供类似查询朋友发出和收到了多少红包金额的功能。
西瓜为了知道自己是不是最土豪的,只好使用了自己的专业技能。西瓜编写了一个黑客小程序,利用这个小程序西瓜知道了在微信上他的好友相互之间的流通金额(双方发出红包金额的总和,比如A给B 6元红包,B给A 4元红包,这样的话A和B之间的流通金额是10元)。现在他想通过这些信息来得知,他自己是否有可能成为他好友之中发出红包金额最多的人(即在微信上发出的红包金额和在支付宝上发出的红包金额最多的人)。
不过他并不知道怎么做,所以他找到了聪明的你,让你来帮他计算是否有这种可能性。
他给你的信息有这些:
1、在支付宝中所有人的支出金额
2、在微信中所有人相互之间的流通金额
现在他想请你帮忙,让你通过他给你的这些信息来判断是否有这样的可能,使得西瓜能成为他好友之中支出金额最大的人。
所有金额的最低单位为分,所有输入均精确到两位小数。并且西瓜期望的是他的支出金额必须严格大于他的好友们。
Input
有多组输入( 组数 <=1024),以EOF结束。
对于每组输入,第一行为两个整数N、M,表示西瓜的好友数量和西瓜知道了多少对好友相互之间的流通金额。
西瓜将他和他的好友编号为1-N, (1 <= N <= 150, 1 <= M <= 7000),西瓜的编号是0。
接下来有M行,每行包含三个数字a, b, c,表示a和b之间的流通金额为c (0 <= a,b <= n, 1 <= c <= 65535)。
西瓜本人以及其他人的流通金额也包含在这M组中。
再接下来的一行,包含N+1个数字,第i个数字表示第i个好友在支付宝中支出的金额(这里的i从0开始,包含西瓜本人)(金额数值<=65536)。
对于每组输入,第一行为两个整数N、M,表示西瓜的好友数量和西瓜知道了多少对好友相互之间的流通金额。
西瓜将他和他的好友编号为1-N, (1 <= N <= 150, 1 <= M <= 7000),西瓜的编号是0。
接下来有M行,每行包含三个数字a, b, c,表示a和b之间的流通金额为c (0 <= a,b <= n, 1 <= c <= 65535)。
西瓜本人以及其他人的流通金额也包含在这M组中。
再接下来的一行,包含N+1个数字,第i个数字表示第i个好友在支付宝中支出的金额(这里的i从0开始,包含西瓜本人)(金额数值<=65536)。
Output
对于每组输入,输出单独一行包含“Yes”或者“No”,表示西瓜是否有可能成为他的好友之中支出金额最大的人。
Sample Input
Original | Transformed |
3 4 1 0 2.12 2 1 4.22 3 1 4.21 3 2 2.12 7.00 0.00 2.10 3.05 3 1 3 2 2.24 1.04 5.28 1.33 3.44
Sample Output
Original | Transformed |
Yes No
解题思路:
先贴一发链接《网络流和棒球赛淘汰模型》
根据这个模型,再靠着rolight菊苣的尽心指导,蒟蒻我终于把这道题ac了。
这道题目,极端情况下会有1w多条边,根据rolight菊苣说的,他把能卡掉的写法都卡掉了。
所以用dinic算法比较好,EK算法似乎也行,不过我没试过。
尽量不要用vector邻接表。这样也有很大可能超时。
来说说这道题吧。主体就是上面链接发的这个模型,还有dinic算法。
其实对于这种模型,有多少场比赛事实上是不需要考虑的,也不需要按照模型里的那个图建图,
对于任意一场比赛,不妨设是a-b的比赛,那么我们直接考虑在a-b之间添加一条边表示a-b要进行多少次比赛。
然后设置超级源点超级汇点计算从超级源点到超级汇点的值就好。
对于这道题目,只需要把两个朋友之间的流通金额当作还未打的比赛,已知的支出金额当作已知的得分,显然套用这个模型之后能够得到答案。
代码:
#include <queue> #include <cstdio> #include <cstring> using namespace std; #define Min(a, b) ((a) < (b) ? (a) : (b)) typedef struct node{ int v, cap, nxt; node(int a = 0, int b = 0, int c = 0): v(a), cap(b), nxt(c){} }Edge; const int maxn = 200; const int maxm = 20000; const int INF = 0x3f3f3f3f; int head[maxm]; Edge edge[maxm]; int n, m, s, t, tot; int dis[maxn], num[maxn], value[maxn][maxn]; void add(int u, int v, int cap){ edge[tot] = Edge(v, cap, head[u]); head[u] = tot++; edge[tot] = Edge(u, 0, head[v]); head[v] = tot++; } int bfs(){ int x; memset(dis, 0, sizeof(dis)); queue<int> q; q.push(s); dis[s] = 1; while(!q.empty()){ x = q.front(); q.pop(); if(x == t) break; for(int i = head[x]; ~i; i = edge[i].nxt){ Edge &e = edge[i]; if(e.cap && dis[e.v] == 0){ q.push(e.v); dis[e.v] = dis[x] + 1; } } } return dis[t]; } int dfs(int x, int f){ if(x == t) return f; int sum = 0; for(int i = head[x]; ~i; i = edge[i].nxt){ Edge &e = edge[i]; if(e.cap && dis[e.v] == dis[x] + 1){ int ret = dfs(e.v, Min(f, e.cap)); sum += ret; f -= ret; e.cap -= ret; edge[i^1].cap += ret; //i ^ 1可以求出i的反向边 } } return sum; } void dinic(){ while(bfs()) dfs(s, INF); } void solve(){ for(int i = 1; i <= n; ++i){ num[0] += value[0][i]; } for(int i = 1; i <= n; ++i){ if(num[0] <= num[i]){ puts("No"); return; } } s = n + 1; t = s + 1; for(int i = 1; i <= n; ++i){ for(int j = 1; j < i; ++j){ if(value[i][j] > 0){ add(s, i, value[i][j]); add(i, j, value[i][j]); } } add(i, t, num[0] - num[i] - 1); } dinic(); for(int i = head[s]; ~i; i = edge[i].nxt){ if(edge[i].cap != 0) { puts("No"); return; } } puts("Yes"); return; } int main(){ int a, b; double f; while(~scanf("%d%d", &n, &m)){ tot = 0; memset(head, -1, sizeof(head)); memset(value, 0, sizeof(value)); for(int i = 0; i < m; ++i){ scanf("%d%d%lf", &a, &b, &f); value[a][b] = (int)(f * 100 + 1e-9); value[b][a] = value[a][b]; } for(int i = 0; i <= n; ++i){ scanf("%lf", &f); num[i] = (int)(f * 100 + 1e-9); } solve(); } return 0; }