题意:给了n个工厂,m个商店,每个商店只有在要求的工厂都建好后才能获得利润,而建一个工厂需要花费和时间,现在问你能获得利润至少为l的最短时间是多少,在这个时间下的最大利润是多少;
题解:因为每一个商店盈利都必须有一些工厂修好。这就是一个闭合图的暗示!
闭合图:一个有向图的子点集,使其中的点的出边都指回集合中的点,则称此为闭合图。
最大权闭合图:给每个点赋上点权,则权和最大的闭合图,为最大权闭合图。
算法(求最大权闭合子图):
(1)新增源点和汇点s,t
(2)对于原图中的边u->v,增加一条u->v的容量为无穷大的边
(3)对于点u,如果w[u]>0,增加一条s->u的容量为w[u]的边;否则增加一条u->v的容量为-w[u]的边
定义一个割划分出的S集合为一个解,那么割集的容量之和就是(未被选的A集合中的顶点的权值 + 被选的B集合中的顶点的权值),记为Cut。A集合中所有顶点的权值之和记为Total,那么Total - Cut就是(被选的A集合中的顶点的权值 - 被选的B集合中的顶点的权值),即为我们的目标函数,记为A。要想最大化目标函数A,就要尽可能使Cut小,Total是固定值,所以目标函数A取得最大值的时候,Cut最小,即为最小割。
用途:闭合图的性质恰好反映了事件之间的必要条件的关系:一个事件发生,它需要的所有前提都要发生。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const ll INFF=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double pi=acos(-1.0);
const double eps=1e-9;
int level[201010];
int iter[201010];
struct node
{
int x,y,c,rev;
};
vector<node>vec[201010];
void add_edge(int x,int y,int c)
{
node p1,p2;
p1.y=y,p1.c=c,p1.rev=vec[y].size();
vec[x].push_back(p1);
p2.y=x,p2.c=0,p2.rev=vec[x].size()-1;
vec[y].push_back(p2);
}
void bfs(int s)
{
memset(level,-1,sizeof(level));
queue<int>q;
level[s]=0;
q.push(s);
while(!q.empty())
{
int v=q.front();
q.pop();
for(int i=0;i<vec[v].size();i++)
{
node p=vec[v][i];
if(p.c>0&&level[p.y]<0)
{
level[p.y]=level[v]+1;
q.push(p.y);
}
}
}
}
int dfs(int v,int t,int f)
{
if(v==t)
return f;
for(int &i=iter[v];i<vec[v].size();i++)
{
node &p=vec[v][i];
if(p.c>0&&level[v]<level[p.y])
{
int d=dfs(p.y,t,min(f,p.c));
if(d>0)
{
p.c-=d;
vec[p.y][p.rev].c+=d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t)
{
int ans=0;
for(;;)
{
bfs(s);
if(level[t]<0)
return ans;
memset(iter,0,sizeof(iter));
int f;
while((f=dfs(s,t,INF))>0)
ans+=f;
}
}
int cost[205],time[205];
int GET[205],num[205],dic[205][205];
int main()
{
int t;scanf("%d",&t);
for(int vv=1;vv<=t;vv++)
{
int n,m,L;scanf("%d%d%d",&n,&m,&L);
for(int i=1;i<=n;i++)scanf("%d%d",&cost[i],&time[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&GET[i],&num[i]);
for(int j=1;j<=num[i];j++)
scanf("%d",&dic[i][j]);
}
int l=0,r=INF;
int anst=-1,ansg=0;
while(r>=l)
{
int mid=(l+r)/2;
for(int i=0;i<=n+m+10;i++)vec[i].clear();
for(int i=1;i<=n;i++)
{
if(mid>=time[i])add_edge(0,i,cost[i]);
else add_edge(0,i,INF);
}
int sum=0;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=num[i];j++)
add_edge(dic[i][j],i+n,INF);
add_edge(i+n,n+m+1,GET[i]);
sum+=GET[i];
}
int ss=max_flow(0,n+m+1);
if(sum-ss>=L)
{
anst=mid;
ansg=sum-ss;
r=mid-1;
}
else
{
l=mid+1;
}
}
printf("Case #%d: ",vv);
if(anst!=-1)
printf("%d %d\n",anst,ansg);
else
puts("impossible");
}
return 0;
}
694

被折叠的 条评论
为什么被折叠?



