参考博客:
http://blog.youkuaiyun.com/queuelovestack/article/details/52222085分析:给出N个工厂,每个工厂给出建造时间和费用,给出M个商店,每个商店给出收益和需要哪些工厂供货才能盈利,求获得L盈利的最小时间(盈利 = 商店的收益 - 工厂的投资费用)
题解:
要求最小时间,所以我们可以先二分搜索时间,因为时间是1~1000000000,所以二分搜并不会超时。每次搜到一个时间后判断这个时间下的最大盈利是否满足要求。判断方法:
我们可以先计算出每一个商店能够盈利的最大时间,即需要的工厂种建造时间最长的那个,这样就可以判断当前时间下该商店能不能盈利;然后再用一个值存储当前已经建造好的工厂的费用。所以该商店的盈利 = 商店收益 - 需要建造工厂总费用 + 已经建好工厂总费用。 每次选择盈利最大的商店后再更新当前未选取商店的已经建好工厂费用。直到当前不能选为止,过程中的最大盈利若超过L则满足,否则不满足。AC代码:
/*************************************************************************
> File Name: 1012.cpp
> Author: Akira
> Mail: qaq.febr2.qaq@gmail.com
> Created Time: 2016年08月16日 星期二 18时44分04秒
************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <set>
#include <list>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))
using namespace std;
#define MaxN 100000
#define MaxM MaxN*10
#define INF 1000000007
#define bug cout<<88888888<<endl;
int T;
int N,M;
int tar;
struct plants
{
int pay,t;//工厂建造的花费和时间
}P[233];
struct shop
{
int pro; //商店的盈利
int K; //商店所需要的工厂
int index[233]; //工厂下标
int time; //工厂建造的最大时间
int cost; //建造工厂的总花费
int finish; //当前已经建好的工厂的花费
}S[233];
struct Edge
{
int v,next;
}edge[66666];
int cont;
int head[233];
void add(int u, int v)
{
edge[cont].v = v;
edge[cont].next = head[u];
head[u] = cont++;
}
void init()
{
cont = 0;
MST(head,-1);
}
int Svis[233];
int Pvis[233];
int judge(int t)
{
int Get = -INF;
CLR(Svis);
CLR(Pvis);
for(int i=1;i<=M;i++)
S[i].finish = 0; //每个商店对应的已建工厂清0
int Max; //记录某次选择商店中的最大盈利
int tmp = 0; //记录总盈利
for(int i=1;i<=M;i++) //一共M个商店
{
Max = -INF;
int choose;
for(int j=1;j<=M;j++) //遍历每一个商店,选择盈利最大的商店
{
if(!Svis[j] && S[j].time<=t) //如果该商店未遍历,且其盈利的最大时间不超过当前t时间,则进行判断
{
if(Max<S[j].pro-S[j].cost+S[j].finish) //如果当前的商店盈利大于之前的选择,则更新这次选定的商店
{
Max = S[j].pro-S[j].cost+S[j].finish; //更新盈利
choose = j; //更新商店下标
}
}
}
if(Max == -INF) break; //说明没有商店可以选了,则跳出
Svis[choose] = 1; //标记选过的商店
tmp+=Max; //更新总盈利
Get = max(tmp, Get); //更新过程中达到的最大盈利
for(int j=1;j<=S[choose].K;j++) //遍历当前选择商店需要的所有工厂
{
if(!Pvis[S[choose].index[j]]) //找到没有建造的工厂
{
Pvis[S[choose].index[j]] = 1; //建造它
for(int l = head[S[choose].index[j]]; l!=-1; l = edge[l].next) //然后通过邻接链表更新没选取的商店的已建造好工厂的费用
{
if(!Svis[edge[l].v]) //找到未选取的商店
S[edge[l].v].finish += P[S[choose].index[j]].pay;
}
}
}
}
if(Get >= tar)
return Get;
return 0;
}
int main()
{
scanf("%d", &T);
int tt = 1;
while(T--)
{
init();
scanf("%d%d%d", &N, &M, &tar);
for(int i=1;i<=N;i++)
{
scanf("%d%d", &P[i].pay, &P[i].t);
}
for(int i=1;i<=M;i++)
{
scanf("%d%d", &S[i].pro, &S[i].K);
S[i].time = S[i].cost = 0;
for(int j=1;j<=S[i].K;j++)
{
scanf("%d", &S[i].index[j]);
add(S[i].index[j], i);
S[i].time = max(S[i].time, P[S[i].index[j]].t);
S[i].cost += P[S[i].index[j]].pay;
}
}
int l=1;
int r=1000000000;
int Min = INF;
int mid;
int ans;
while(l<=r)
{
mid = (l+r)/2;
int tmp = judge(mid);
if(tmp)
{
ans = tmp;
Min = min(mid, Min);
r = mid-1;
}
else
l = mid+1;
}
if(Min != INF)
printf("Case #%d: %d %d\n", tt++, Min, ans);
else
printf("Case #%d: impossible\n", tt++);
}
return 0;
}