ZOJ 2071 Technology Trader (找割边)
继续我的图论专题练习,这次翻到ZOJ上面的一题,题目略长,前面的大体意思都比较好理解。大意为,某老板接了许多订单,需要制造,因为就需要原材料,现在给定仓库中的原材料和其成本,再列出订单和其售价和原材料需求,求出最大的收益是多少,并列出需要制造的订单有多少件,原材料要多少个,并列出材料的名称。
初看感觉很难,不知道如何下手,后来看到提示是找割边,正确意义上讲感觉是找割边。
将0设为源点,和原材料相连,权值为材料的成本价,将订单和汇点T相连,权值为售价,订单和其需要的部件相连,权值无限,只要订单到汇点满流,就认为订单不可取(售价-成本都成0了,那就没必要做了),只有非满的边才可以取,所以说是找割边。
最后找边输出就好!附上代码!注释挺详细的了!
#include <iostream> #include <stdio.h> #include <string.h> using namespace std; const int maxn=500; const int maxm=500; const int INF=0xffffff; struct edge { int from,to,val,next; }map[maxn]; struct compenents //定义结构体 可设为部件 或者订单 { char name[50]; int price; int pennumber; //如果是订单 需要记录其需要多少部件 char penname[500][50]; //各个部件的名字 }pen[500]; int vis[maxn],que[maxn],dist[maxn],len; void init() //初始化 切记 { len=0; memset(vis,-1,sizeof(vis)); } void insert (int from,int to,int val) { map[len].from=from,map[len].to=to,map[len].val=val; map[len].next=vis[from]; vis[from]=len++; map[len].from=to,map[len].to=from,map[len].val=0; map[len].next=vis[to]; vis[to]=len++; } int Dinic(int n,int s,int t) { int ans=0; while(true) { int head,tail,id,i; head=tail=0; que[tail++]=s; memset(dist,-1,sizeof(dist)); dist[s]=0; while(head<tail) { id=vis[que[head++]]; while(id!=-1) { if(map[id].val>0&&dist[map[id].to]==-1) { dist[map[id].to]=dist[map[id].from]+1; que[tail++]=map[id].to; if(map[id].to==t) { head=tail; break; } } id=map[id].next; } } if(dist[t]==-1) break; id=s,tail=0; while(true) { if(id==t) //找到一条增广路 { int flow=INF,fir; for(i=0;i<tail;i++) if(map[que[i]].val<flow) { fir=i; flow=map[que[i]].val; } for(i=0;i<tail;i++) map[que[i]].val-=flow,map[que[i]^1].val+=flow; ans+=flow; tail=fir; id=map[que[fir]].from; } id=vis[id]; while(id!=-1) { if(map[id].val>0&&dist[map[id].from]+1==dist[map[id].to]) break; id=map[id].next; } if(id!=-1) { que[tail++]=id; id=map[id].to; } else { if(tail==0) break; dist[map[que[tail-1]].to]=-1; id=map[que[--tail]].from; } } } return ans; } //以上是纯模板 只要知道输入 Dinic 的三个int 是点数 起点 汇点 int main() { int t,n,m,i,k,j; scanf("%d",&t); int sum; while(t--) { init(); sum=0; scanf("%d",&n); for(i=1;i<=n;i++) //建图 { cin>>pen[i].name>>pen[i].price; insert(0,i,pen[i].price); } scanf("%d",&m); for(i=n+1;i<=n+m;i++) //输入订单 { cin>>pen[i].name>>pen[i].price>>pen[i].pennumber; sum+=pen[i].price; //记录总价值 insert(i,n+m+1,pen[i].price); for(j=1;j<=pen[i].pennumber;j++) //输入需要的部件名称 { cin>>pen[i].penname[j]; for(k=1;k<=n;k++) if(strcmp(pen[i].penname[j],pen[k].name)==0) //部件和订单连线 { insert(k,i,INF); break; } } } printf("%d\n",sum-Dinic(n+m+2,0,n+m+1)); //输出最大收益 int cnt1,cnt2; cnt1=cnt2=0; int res[250]; for(i=n+1;i<=n+m;i++) //找割边 { for(j=vis[i];j!=-1;j=map[j].next) { if(map[j].to==n+m+1) { if(map[j].val) res[cnt1++]=i; //记录 break; } } } printf("%d\n",cnt1); //输出制造个数 for(i=0;i<cnt1;i++) { cout<<pen[res[i]].name<<endl; cnt2+=pen[res[i]].pennumber; } printf("%d\n",cnt2); //输出需要的部件个数 for(i=0;i<cnt1;i++) //列出部件 for(j=1;j<=pen[res[i]].pennumber;j++) cout<<pen[res[i]].penname[j]<<endl; puts(""); } return 0; }