您的位置:首页 > 其它

ZOJ 2071 Technology Trader (找割边)

2011-08-16 10:49 225 查看
继续我的图论专题练习,这次翻到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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: