关于jzyzoj——P1341:被污染的牛奶的题解探讨

  题面:

描述 Description
     你第一天接手三鹿牛奶公司就发生了一件倒霉的事情:公司不小心发送了一批有三聚氰胺的牛奶。很不幸,你发现这件事的时候,有三聚氰胺的牛奶已经进入了送货网。这个送货网很大,而且关系复杂。你知道这批牛奶要发给哪个零售商,但是要把这批牛奶送到他手中有许多种途径。送货网由一些仓库和运输卡车组成,每辆卡车都在各自固定的两个仓库之间单向运输牛奶。在追查这些有三聚氰胺的牛奶的时候,有必要保证它不被送到零售商手里,所以必须使某些运输卡车停止运输,但是停止每辆卡车都会有一定的经济损失。你的任务是,在保证坏牛奶不送到零售商的前提下,制定出停止卡车运输的方案,使损失最小。
输入格式 Input Format
     第一行: 两个整数N(2<=N<=32)、M(0<=M<=1000), N表示仓库的数目,M表示运输卡车的数量。仓库1代 表发货工厂,仓库N代表有三聚氰胺的牛奶要发往的零售商。 第2..M+1行: 每行3个整数Si,Ei,Ci。其中Si,Ei表示这 辆卡车的出发仓库,目的仓库。Ci(0 <= C i <= 2,000,000) 表示让这辆卡车停止运输的损失。
输出格式 Output Format
     第1行两个整数c、t,c表示最小的损失,T表示要停止的最少卡车数。接下来t 行表示你要停止哪几条线路。如果有多种方案使损失最小,输出停止的线路最少的方案。如果仍然还有相同的方案,请选择开始输入顺序最小的。
样例输入 Sample Input    

4 5
1 3 100
3 2 50
2 4 60
1 2 40
2 3 80 

样例输出 Sample Output    

60 1
3

时间限制 Time Limitation
     1s

  首先这道题的建图模型是不用考虑的,直接按照数据建就行。关键是如何在求出最小割之后求出对应的割集中包含的边的个数以及各个边的编号呢?

  这时候会有一种类似正解的做法:求得最大流之后遍历残余网络中的所有边,对权值为0的边进行输出。

  OJ上的数据较水,用这种方法就直接AC了,但是考虑一下下面的这个图:

  (红色手写的数字为边权值,S为源点,T为汇点,边按照所标记的字典序进行读入)

  那么显然我们会输出j与k,但显然正解是n

  所以这种方法只能水过较水的数据,如果用心构造数据的话可以把这样的算法卡掉。

  所以我们要考虑一个更加正确的算法:

    题目中给出了边数的最大值:1000,那么最小割所包含的边数最多为1000个,因此我们可以把每条边的权值乘上1001在+1,那么求得的maxflow/1001就是最大流,%1001就是边的个数。剩下的还有求出最小割所包含的边,怎么办?我们可以枚举所有的边,然后每次枚举的时候恢复网络,并将枚举到的边权值设为0,如果最大流的变化值等于边权值,那么这个边显然在最小割中。

    但事实上如果用邻接表的话只这样做还会超时,因为第6组数据是1000个(1,4,2000000)的边,每次枚举边权好像还会超时。。。但是我们还可以根据最小割的性质来进行剪枝:当我们找到一个包含在最小割里面的边时,可以不恢复这个边的权值,同时更新一下所求得的最大流。

    这样就能AC了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cctype>
 6 #include<ctime>
 7 using namespace std;
 8 int rev[1000010],head[10010],len=0,n,m,forward,queue[10010],h=0,tail=1,level[10010];
 9 long long sum=0,ans;
10 bool vis[10010],check=true;
11 #define oo 0x7fffffff
12 struct node{
13     int y,ne;
14     long long v;
15 }edge[1000010];
16
17 void addedge(int x,int y,long long v){
18     edge[++len].y=y;    edge[len].v=v;    edge[len].ne=head[x];    head[x]=len;    rev[len]=len+1;
19     edge[++len].y=x;    edge[len].v=0;    edge[len].ne=head[y];    head[y]=len;    rev[len]=len-1;
20 }
21
22 int read(){
23     int x=0;char ch=getchar();
24     while (!isdigit(ch))    ch=getchar();
25     while (isdigit(ch)){x=x*10+ch-‘0‘;    ch=getchar();}
26     return x;
27 }
28
29 bool make_level(){
30     h=0;    tail=1;
31     memset(level,-1,sizeof(level));
32     queue[1]=0;    level[0]=0;
33     while (h++<tail){
34         int tn=queue[h];
35         for (int i=head[tn];i;i=edge[i].ne)
36             if (edge[i].v&&level[edge[i].y]<0){
37                 queue[++tail]=edge[i].y;
38                 level[edge[i].y]=level[tn]+1;
39             }
40     }
41     return level[n]>=0;
42 }
43
44 long long max_flow(int s,long long flow){
45     if (s==n)    return flow;
46     long long maxflow=0,d=0;
47     for (int i=head[s];i&&maxflow<flow;i=edge[i].ne)
48         if (level[edge[i].y]==level[s]+1&&edge[i].v)
49             if (d=max_flow(edge[i].y,min(flow-maxflow,edge[i].v))){
50                 maxflow+=d;
51                 edge[i].v-=d;
52                 edge[rev[i]].v+=d;
53             }
54     if (!maxflow)    level[s]=-1;
55     return maxflow;
56 }
57
58 void dinic(){
59     long long d;
60     while (make_level())
61         while (d=max_flow(1,oo))sum+=d;
62 }
63
64 void init(){
65     n=read();    m=read();
66     int x,y,v;
67     for (int i=1;i<=m;i++){
68         x=read();    y=read();    v=read();
69         addedge(x,y,v*1001+1);
70     }
71     addedge(0,1,200000000001001LL);
72 }
73
74 int main(){
75     init();
76     dinic();
77     long long cnt=sum/1001LL;
78     cnt=cnt*1001LL;
79     cnt=sum-cnt;
80     ans=sum;
81     printf("%lld %lld\n",ans/1001,cnt);
82     for (int i=1;i<=m&&cnt;i++){
83         sum=0;    check=true;
84         for (int j=1;j<=len;j+=2){edge[j].v+=edge[rev[j]].v;    edge[rev[j]].v=0;}
85         long long tmp=edge[i*2-1].v;
86         edge[i*2-1].v=0;
87         dinic();
88         if (ans-sum==tmp){
89             printf("%d\n",i);
90             cnt--;
91             ans=sum;
92         }
93         else    edge[i*2-1].v=tmp;
94     }
95     return 0;
96 }

AC代码1

  用以上的方法来做,第6组也需要600MS的时间跑过。。。

  我们可以考虑一下另外的对边的处理方法。

  如果不将边权值*1001+1的话,就不需要过多的关于longlong的操作了,那么我们可以省去很大的时间。

  但是如何求解呢?

  最大流直接套用模板就能求出,但是如何求边的个数以及边的编号呢?

  用贪心的方法得证,按照边权值从大到小进行遍历的话,所求得的边数是最小的,那么我们可以对于每个边再用一个结构体e来存储,e.id表示边的编号,e.v表示边权值,那么我们排序一遍之后,依次进行遍历,就能得到最小割的边的个数以及边的编号。

  这样的话我们就极大地节省了longlong运算的时间。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cctype>
 6 #define oo 0x7fffffff
 7 using namespace std;
 8 int head,tail,level[110],q[110],s,t,sum=0,rev[10010],len=0,lin[110];
 9 int m,n,Id[10010],cnt=0,ans;
10 struct node{
11     int y,v,ne;
12 }edge[10010];
13
14 struct node1{
15     int v,id;
16 }e[10010];
17
18 inline bool mycmp(node1 a,node1 b)
19 {return a.v>b.v||a.v==b.v&&a.id<b.id;}
20
21 inline bool mycmp1(int a,int b)
22 {return a<b;}
23 void addedge(int x,int y,int v){
24     edge[++len].y=y;edge[len].v=v;edge[len].ne=lin[x];lin[x]=len;rev[len]=len+1;
25     edge[++len].y=x;edge[len].v=0;edge[len].ne=lin[y];lin[y]=len;rev[len]=len-1;
26 }
27
28 int read(){
29     int x=0,y=1;char ch=getchar();
30     while (!isdigit(ch)){if (ch==‘-‘)y=-1;ch=getchar();}
31     while (isdigit(ch)){x=x*10+ch-‘0‘;ch=getchar();}
32     return x*y;
33 }
34
35 bool make_level(){
36     head=0,tail=1;q[1]=s;memset(level,-1,sizeof(level));level[s]=0;
37     while (head++<tail){
38         int tn=q[head];
39         for (int i=lin[tn],y;i;i=edge[i].ne)
40             if (edge[i].v&&level[y=edge[i].y]==-1){
41                 level[y]=level[tn]+1;
42                 q[++tail]=y;
43             }
44     }
45     return level[t]!=-1;
46 }
47
48 int max_flow(int k,int flow){
49     if (k==t)    return flow;
50     int maxflow=0,d;
51     for (int i=lin[k];i&&maxflow<flow;i=edge[i].ne)
52         if (edge[i].v&&level[edge[i].y]==level[k]+1)
53             if (d=max_flow(edge[i].y,min(flow-maxflow,edge[i].v))){
54                 edge[i].v-=d;edge[rev[i]].v+=d;maxflow+=d;
55             }
56     if (!maxflow)level[k]=-1;
57     return maxflow;
58 }
59
60 void dinic(){
61     int d;
62     while (make_level())    while (d=max_flow(s,oo))    sum+=d;
63 }
64
65 void init(){
66     n=read();m=read();
67     s=1,t=n;
68     int x,y,v;
69     for (int i=1;i<=m;i++){
70         x=read();y=read();v=read();
71         addedge(x,y,v);
72     }
73 }
74
75 int main(){
76     init();
77     dinic();
78     printf("%d ",sum);
79     ans=sum;
80     for (int j=1;j<=len;j+=2){edge[j].v+=edge[rev[j]].v;edge[rev[j]].v=0;}
81     for (int i=1;i<=m;i++){e[i].v=edge[i*2-1].v;e[i].id=i;}
82     sort(e+1,e+1+m,mycmp);
83     for (int i=1;i<=m&&ans;i++){
84         int id=e[i].id,v=e[i].v;
85         sum=0;
86         edge[id*2-1].v=0;
87         dinic();
88         for (int j=1;j<=len;j+=2){edge[j].v+=edge[rev[j]].v;edge[rev[j]].v=0;}
89         if (ans-sum==v){
90             ans=sum;
91             Id[++cnt]=id;
92         }
93         else edge[id*2-1].v=v;
94     }
95     printf("%d\n",cnt);
96     sort(Id+1,Id+1+cnt);
97     for (int i=1;i<=cnt;i++)    printf("%d\n",Id[i]);
98     return 0;
99 }

AC代码2

  用这种方法每组数据都能在100ms之内跑过。

  对于网络流来说,我们不仅仅需要考虑的是建图的方式,还有关于边的各种考虑,大概这就是其难度所在吧= =

时间: 2024-08-14 05:03:47

关于jzyzoj——P1341:被污染的牛奶的题解探讨的相关文章

Ohl&#224;l&#224;

Chap 1数数字 un 1 deux 2 trois 3 quatre 4 cinq 5 six 6 sept 7 huit 8 neuf 9 dix 10 Chap 2 讲地名 Paris 巴黎 Lyon 里昂 Bordeaux 波尔多 Marseille 马赛 Grenoble 格勒诺布尔 Lille 里尔 Orléans 奥尔良 Evian 依云 Chap 3 中国地名法语发音 Pékin 北京 Shanghai 上海 Canton 广州 Hong Kong 香港 Nankin 南京 C

59%亚洲制造企业暂未引入人工智能, 但这场求变风险值得一试

文章发布于公号[数智物语] (ID:decision_engine),关注公号不错过每一篇干货. 转自 | 机器之能 编者按: 我们发现,制造业是一个受人工智能技术青睐,但却不会轻易求变的行业. 一方面,是这项技术处于应用初期,许多制造业领袖们并没有看到人工智能技术为企业带来的普遍的.显著的效益: 而另一方面,现有数据的不可用性与专有技术人才短缺,限制了企业领导者们与工人们的想象力. 2019年4月1日,微软亚洲与市场分析公司 IDC 亚太部门在新加坡的 Media OutReach 上发布了针

牛奶可乐经济学(一)

解开人类日常行为之谜 取款机制造商必须给普通的街头取款机装配带点字盲文的小键盘,因为所有机器都造成一个样子,成本更低廉.要不然的话就要把两类机器都分开,保证合适的机器安装到合适的地方. 生产两种不同提款机的成本,远远大于合理的预期收益. 机会成本 从事一项活动的机会成本,是指你为了从事这件事而放弃其他事情的价值. 为什么曼哈顿的居民大多粗鲁没有耐心,而堪萨斯首府托皮卡的居民却友善而谦恭呢? 曼哈顿人的工资水平最高,要做的事情很多,时间的机会成本很高. 有例外才能证明规律的存在. 成本效益原则 成

洛谷 P1208 [USACO1.3]混合牛奶 Mixing Milk

题目描述 由于乳制品产业利润很低,所以降低原材料(牛奶)价格就变得十分重要.帮助Marry乳业找到最优的牛奶采购方案. Marry乳业从一些奶农手中采购牛奶,并且每一位奶农为乳制品加工企业提供的价格是不同的.此外,就像每头奶牛每天只能挤出固定数量的奶,每位奶农每天能提供的牛奶数量是一定的.每天Marry乳业可以从奶农手中采购到小于或者等于奶农最大产量的整数数量的牛奶. 给出Marry乳业每天对牛奶的需求量,还有每位奶农提供的牛奶单价和产量.计算采购足够数量的牛奶所需的最小花费. 注:每天所有奶农

DNS污染和DNS劫持的解决办法

DNS污染是指一些刻意制造或无意中制造出来的域名服务器分组,把域名指往不正确的IP地址. DNS劫持又称域名劫持,是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能访问或访问的是假网址. DNS污染解决方法 1.使用各种SSH加密代理,在加密代理里进行远程DNS解析,或者使用VPN上网. 2.修改hosts文件,操作系统中Hosts文件的权限优先级高于DNS服务器,操作系统在访问某个域名

本地文件包含被污染的SSH日志GetShell

0x01 前言 我们在渗透测试过程中常常会通过文件包含来getshell,一般会包含恶意代码的图片.污染UA或referer等HTTP头从而包含访问日志等等.这里介绍另外一种包含的方法,通过污染SSH日志的方式. 0x02 SSH日志污染 使用测试环境为ubuntu(10.168.33.174),ssh日志默认是在/var/log/auth.log下,默认其它用户是有read的权限的.然后我们直接执行ssh '<?php system($_GET[c]);?>'@10.168.33.174可以

HTTP参数污染

HTTP Parameter Pollution简称HPP,所以有的人也称之为“HPP参数污染”. 一篇很不错关于HPP参数污染的文章:http://www.paigu.com/a/33478/23535461.html 如文章中所言,HPP并非一个漏洞,但是网站存在SQL或者XSS,在有WAF的情况之下可以帮助黑客进行绕过WAF. 那么什么是HPP参数污染呢? 原本的正常搜索URL是:https://www.baidu.com/s?ie=UTF-8&wd=珍惜少年时博客 我再添加一个wd参数:

洛谷P3093 [USACO13DEC]牛奶调度Milk Scheduling

题目描述 Farmer John has N cows that need to be milked (1 <= N <= 10,000), each of which takes only one unit of time to milk. Being impatient animals, some cows will refuse to be milked if Farmer John waits too long to milk them. More specifically, cow

母亲的牛奶(milk) (BFS)

问题 A: 母亲的牛奶(milk) 时间限制: 1 Sec  内存限制: 64 MB提交: 14  解决: 8[提交][状态][讨论版] 题目描述 农民约翰有三个容量分别是A.B.C升的桶,A.B.C分别是三个从1到20的整数,最初,A和B桶都是空的,而C桶是装满牛奶的.有时,约翰把牛奶从一 个桶倒到另一个桶中,直到被灌桶装满或原桶空了.当然每一次灌注都是完全的,由于节约,牛奶不会丢失.写一个程序去帮助约翰找出当A桶是空的时候,C桶中 牛奶所剩量的所有可能性. 输入 单独的1行,包括三个整数A,