题目:
http://acm.hdu.edu.cn/showproblem.php?pid=6437
题意:
一天n小时,m个电影,k个人(可以有人不去看电影);
电影i放映时间在第si~ti小时,电影有个属性(A or B);
电影i有快乐值wi;
连续看同一属性的电影会产生不快乐值W;
求所有人的快乐值之和最大;
分析:
注意到一个结论:越多人看电影越好,把人数当流量,就可以求最大流下的最小费用;
由于m很小,所以可以O(m^2)地处理出各电影间的连边;
若y电影在x电影之后放映,且不同类,则从x到y连一条流量为1,费用为wy的边;
若y电影在x电影之后放映,且同类,则从x到y连一条流量为1,费用为wy-W的边;
然后建立一个起点,分别连到各个电影点一条流量为1,费用为wi的边;
同时建立一个终点,分别由各电影点连到终点一条流量为1,费用为0的边;
由于一个电影只能一个人观看,所以把电影点拆分成两个点,所有连到电影i的边全都连到i的汇点上,所有由电影i连出的边全都由i的出点连出,并且把每个i的汇点到出点连一条流量为1,费用为0的边;
把所有边的费用 * -1,求最小费用,然后对总最小费用 * -1,即得到最大费用;
代码:
#include
using namespace std;
typedef long long ll;
const int tmax=450;
const int inf=0x3f3f3f3f;
int Start[tmax],End[tmax],wlike[tmax],op[tmax];
struct Edge{
int from,to,cap,flow,cost;
};
vector edges;
vector<int> G[tmax];
queue<int> Q;
int inq[tmax],d[tmax],p[tmax],a[tmax];
int n,wsad,mm;
void init()
{
int i;
for(i=0;i<=n;i++) G[i].clear();
edges.clear();
mm=0;
return;
}
void add_edge(int from,int to,int cap,int cost)
{
edges.push_back((Edge){from,to,cap,0,cost});
edges.push_back((Edge){to,from,0,0,-cost});
mm=edges.size();
G[from].push_back(mm-2);
G[to].push_back(mm-1);
return;
}
bool BellmanFord(int s,int t,int &flow,int &cost)
{
for(int i=0;i<=n;i++) d[i]=inf;
memset(inq,0,sizeof(inq));
d[s]=0;inq[s]=1;p[s]=0;a[s]=inf;
Q.push(s);
while(!Q.empty())
{
int u=Q.front();Q.pop();
inq[u]=0;
for(int i=0;iif(e.cap>e.flow&&d[e.to]>d[u]+e.cost)
{
d[e.to]=d[u]+e.cost;
p[e.to]=G[u][i];
a[e.to]=min(a[u],e.cap-e.flow);
if(!inq[e.to])
{
Q.push(e.to);
inq[e.to]=1;
}
}
}
}
if(d[t]==inf) return false;
flow+=a[t];
cost+=d[t]*a[t];
int u=t;
while(u!=s)
{
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
u=edges[p[u]].from;
}
//test();
return true;
}
int Mincost(int s,int t)
{
for(int i=0;i1;
int flow=0,cost=0;
while(BellmanFord(s,t,flow,cost));
return -cost;
}
int main()
{
int T,i,j,hour,m,k;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d",&hour,&m,&k,&wsad);
for(i=1;i<=m;i++)
scanf("%d%d%d%d",&Start[i],&End[i],&wlike[i],&op[i]);
for(i=1;i<=m;i++)
for(j=i+1;j<=m;j++)
{
if(End[i]<=Start[j])
{
if(op[i]!=op[j])
add_edge(i+m+1,j,1,wlike[j]);
else add_edge(i+m+1,j,1,wlike[j]-wsad);
}
else if(End[j]<=Start[i])
{
if(op[i]!=op[j])
add_edge(j+m+1,i,1,wlike[i]);
else add_edge(j+m+1,i,1,wlike[i]-wsad);
}
}
for(i=1;i<=m;i++)
add_edge(0,i,1,wlike[i]); //0汇点
for(i=1;i<=m;i++)
add_edge(i+m+1,m+1,1,0); //m+1 终点
n=m*2+5;
add_edge(n,0,k,0);
for(i=1;i<=m;i++)
add_edge(i,i+m+1,1,0);
printf("%d\n",Mincost(n,m+1));
init();
}
return 0;
}