1. 程式人生 > >ACM-ICPC 2018 焦作賽區網路預賽 F. Modular Production Line(最小費用流)

ACM-ICPC 2018 焦作賽區網路預賽 F. Modular Production Line(最小費用流)

題目連結

題意:

給你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);



	}



}