版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37025443/article/details/83117792
题意:
给你n个点,每一个点最多被用k次,每一次任务需要同时使用连续编号的若干个点,任务完成相应获得价值,
并且所使用的点的使用次数-1,问你最大的价值多少
解析:
想了一个下午,想不出怎么建图,甚至怀疑是不是用最小费用流做的,后来晚上灵光一现。
就最普通的先建一条0-cm+2的链(0:源点,cm+2汇点),链上的边的流量都为K(cm是缩完点的点的数量),然后每一个任务的持续持续时间u,v
建立一条u->v+1的权值为w+1的边(v+1是因为避免一个点是一个任务的开头,同时是另一个任务的结尾的情况)
然后跑一遍最小费用流就可以了。
这里的原理我总结成两句话1.任务交叉就共用流量k 2.任务不交叉就并用流量k
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define INFINITE 1 << 26
#define INF 0x3f3f3f3f
#define MAX_NODE 420
#define MAX_EDGE_NUM 80005
struct Edge{
int to;
int vol;
int cost;
int next;
};
Edge gEdges[MAX_EDGE_NUM];
int gHead[MAX_NODE];
int gPre[MAX_NODE];
int gPath[MAX_NODE];
int gDist[MAX_NODE];
int has[100010];
int qe[MAX_NODE][3];
int gEdgeCount;
void InsertEdge(int u, int v, int vol, int cost){
gEdges[gEdgeCount].to = v;
gEdges[gEdgeCount].vol = vol;
gEdges[gEdgeCount].cost = cost;
gEdges[gEdgeCount].next = gHead[u];
gHead[u] = gEdgeCount++;
gEdges[gEdgeCount].to = u;
gEdges[gEdgeCount].vol = 0; //vol为0,表示开始时候,该边的反向不通
gEdges[gEdgeCount].cost = -cost; //cost 为正向边的cost相反数,这是为了
gEdges[gEdgeCount].next = gHead[v];
gHead[v] = gEdgeCount++;
}
//假设图中不存在负权和环,SPFA算法找到最短路径/从源点s到终点t所经过边的cost之和最小的路径
bool Spfa(int s, int t){
memset(gPre, -1, sizeof(gPre));
memset(gDist, -INF, sizeof(gDist));
gDist[s] = 0;
queue<int> Q;
Q.push(s);
while (!Q.empty()){//由于不存在负权和环,因此一定会结束
int u = Q.front();
Q.pop();
for (int e = gHead[u]; e != -1; e = gEdges[e].next){
int v = gEdges[e].to;
if (gEdges[e].vol > 0 && gDist[u] + gEdges[e].cost > gDist[v]){
gDist[v] = gDist[u] + gEdges[e].cost;
gPre[v] = u; //前一个点
gPath[v] = e;//该点连接的前一个边
Q.push(v);
}
}
}
if (gPre[t] == -1) //若终点t没有设置pre,说明不存在到达终点t的路径
return false;
return true;
}
int MinCostFlow(int s, int t){
int cost = 0;
int flow = 0;
while (Spfa(s, t)){
int f = INFINITE;
for (int u = t; u != s; u = gPre[u]){
if (gEdges[gPath[u]].vol < f)
f = gEdges[gPath[u]].vol;
}
flow += f;
cost += gDist[t] * f;
for (int u = t; u != s; u = gPre[u]){
gEdges[gPath[u]].vol -= f; //正向边容量减少
gEdges[gPath[u]^1].vol += f; //反向边容量增加
}
}
return cost;
}
int dnum[MAX_NODE];
int main()
{
int t,n;
int S,Final;
scanf("%d",&t);
int m,K,w;
while(t--)
{
memset(gHead,-1,sizeof(gHead));
gEdgeCount=0;
//memset(has,0,sizeof(has));
scanf("%d%d%d",&n,&K,&m);
int cnt=1;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&qe[i][0],&qe[i][1],&qe[i][2]);
dnum[cnt++]=qe[i][0];
dnum[cnt++]=qe[i][1];
}
sort(dnum+1,dnum+cnt);
has[dnum[1]]=1;
int cm=1;
for(int i=2;i<cnt;i++)
{
if(dnum[cm]!=dnum[i])
{
dnum[++cm]=dnum[i];
has[dnum[i]]=cm;
}
}
S=0;
Final=cm+2;
InsertEdge(0,1,K,0);
for(int i=1;i<=cm+1;i++)
{
InsertEdge(i,i+1,K,0);
}
//int task=Final+1;
for(int i=1;i<=m;i++)
{
int u,v,w;
u=has[qe[i][0]];
v=has[qe[i][1]];
w=qe[i][2];
v++;
//InsertEdge(u,task,1,0);
//InsertEdge(task,v,1,w);
InsertEdge(u,v,1,w);
//task++;
}
int ans=MinCostFlow(S,Final);
printf("%d\n",ans);
}
}