ACM-ICPC 2018 焦作赛区网络预赛 F. Modular Production Line(最小费用流)

版权声明:本文为博主原创文章,未经博主允许不得转载。 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);



	}



}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/83117792
今日推荐