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;
}
posted @ 2011-08-16 10:49  Lxsec  阅读(644)  评论(1编辑  收藏  举报