POJ 3436 http://poj.org/problem?id=3436 特殊解
大概题意,每个机器有P个组件组成,现在给你M个机器的信息,问你最多能组装多少个电脑。
没行第一个参数 能容纳多少台电脑(可以看成网络流中,没条路的容量)
接下来有2P个参数 0 表示不需要 1表示必须有 2可以可有可无第2~p个参数 分别是安装这个电脑前需要的的条件
第p+1个参数到2P个参数表示 安装好后的机器具备那些组件例1测试数据:
3 4
15 0 0 0 0 1 0
10 0 0 0 0 1 1
30 0 1 2 1 1 1
3 0 2 1 1 1 1
第一台机器可以装容纳15台机器,生产条件是全0(红色部分) 生产结果是(绿色部分) 这里只有第3第4台机器可以把整台电脑安装好,而进入机器3需要条件 0 1 2也就是第二个部件必须有,显然刚由1生产过的电脑能送到机器3组装成完整的电脑
这里我们可以采用拆点的方法去建立一个图来进行最短增广路得出结果当然需要有一个超级汇点和超级源点,显然把生产条件都是0的与超级源点相连,生产结果全为1的与超级汇点相连 权值当然是无穷大。然后把每台机器的生产条件和生产结果连接起来,因为在同一台机器。当然是连通的拉!权值当然是自己所能容纳的量机器之间怎么连接?00 11 21 12都可以匹配,而01 10就不能匹配,所以我们就可以轻易得出结论同部件相加等于1的机器不能相连;相连的机器权值为无穷大,这样我们的图就建好了!然后就可以用spfa,EK,dinic等算法解决,我这里用的是dinic
#include<iostream>
#include<queue>
using namespace std;
const int N=505;
const int INF=999999999;
struct Node //定义结构体用来存输入的数据
{
int in[20];
int out[20];
int v;
}node[N];
struct Edge //邻接表
{
int no,to,v,u,next; //no是号码,方便输出,to是目标,v是用来增广路用的,u用来输出时检查是否有连接
}edge[N];
int p,m,s,t,num;
int level[N],head[N]; //划分层次网络数组与邻接表头
int min(int a,int b)
{
return a<b?a:b;
}
void add(int from,int to,int v,int no) //加边
{
edge[num].to=to;
edge[num].v=v;
edge[num].u=v;
edge[num].no=no;
edge[num].next=head[from];
head[from]=num++;
edge[num].to=from;
edge[num].v=0;
edge[num].no=no;
edge[num].u=0;
edge[num].next=head[to];
head[to]=num++;
}
bool judge(int a[],int b[]) //判断两个机器是否可以相连
{
for(int i=0;i<p;i++)
if(a[i]+b[i]==1) return false;
return true;
}
bool BFS() //广搜划分层次网络
{
memset(level,255,sizeof(level));
queue<int> q;
q.push(s);
level[s]=1;
while(!q.empty()&&level[t]==-1)
{
int v=q.front();
q.pop();
for(int i=head[v];i!=-1;i=edge[i].next)
{
if(level[edge[i].to]==-1&&edge[i].v>0)
{
level[edge[i].to]=level[v]+1;
q.push(edge[i].to);
}
}
}
return level[t]!=-1;
}
int DFS(int x,int sum) //递归增加容量
{
if(x==t) return sum;
int temp=sum;
for(int i=head[x];i!=-1;i=edge[i].next)
{
if(level[x]+1==level[edge[i].to]&&edge[i].v>0)
{
int a=DFS(edge[i].to,min(edge[i].v,sum));
edge[i].v-=a;
edge[i^1].v+=a;
sum-=a;
}
}
return temp-sum;
}
int main()
{
int i,j;
bool flag;
while(scanf("%d%d",&p,&m)!=EOF)
{
s=2*m+1; //超级原点和汇点
t=2*m+2;
num=0;
memset(head,-1,sizeof(head));
for(i=1;i<=m;i++)
{
scanf("%d",&node[i].v);
flag=false;
for(j=0;j<p;j++)
{
scanf("%d",&node[i].in[j]);
if(node[i].in[j]==1) flag=true;
}
if(flag==false) add(s,i,INF,i); //建立原点到机器的边
flag=false;
for(j=0;j<p;j++)
{
scanf("%d",&node[i].out[j]);
if(node[i].out[j]==0) flag=true;
}
if(flag==false) add(i+m,t,INF,INF); //建立机器到汇点的边
}
for(i=1;i<=m;i++)
{
add(i,i+m,node[i].v,i); //自己到自己的边
for(j=1;j<=m;j++)
{
if(i==j) continue;
if(judge(node[i].out,node[j].in))
add(i+m,j,INF,j); //A机器到B机器的边
}
}
int ans=0;
while(BFS()) //dinic
{
ans+=DFS(s,INF);
}
int many=0;
int out[N][3];
for(i=m;i<=m+m;i++) //输出
{
for(j=head[i];j!=-1;j=edge[j].next)
{
if(i==edge[j].no||edge[j].no==INF) continue;
if(edge[j].v<edge[j].u) //如果该路容量有变,说明有连接,于是存起来输出
{
out[many][0]=i-m;
out[many][1]=edge[j].no;
out[many][2]=edge[j].u-edge[j].v;
many++;
}
}
}
printf("%d %d\n",ans,many);
for(i=0;i<many;i++)
{
printf("%d %d %d\n",out[i][0],out[i][1],out[i][2]);
}
}
return 0;
}