您的位置:首页 > 理论基础 > 计算机网络

poj 3422 费用流

2013-05-09 16:52 357 查看
题目描述:

题目比较短。选择一个矩阵里,从左上角到右下角的,前k条路径的最大和。

解题思路:

   和网络流思想类似。因为贪心选择第一条最大之后,继续选择的第二条最大,并不意味着两条和最大。因而需要最大流的反向更新的思想。

   构图上,因为网络流的流量,费用是在边上。这里是点。所以需要做拆点构图。

代码:

#include <stdio.h>
#include <queue>
#define N 2510
#define MIN -10000000

using namespace std;

typedef struct EDGE{
int id;
int flow, fee;
struct EDGE *next;
}EDGE;

typedef struct NODE{
int edge_num;
struct EDGE *next;
}NODE;

int tr[51][51], n, k, start, end, sum=0, pre[2*N];
NODE dag[2*N];

void spfa(){ // from start to end
int fee[2*N], i, visit[2*N], v;
for(i=0; i<2*N; i++)
fee[i] = MIN;
fee[start] = 0;
//memset(visit,0,sizeof(visit));
for(i=0;i<2*N;i++)
visit[i] = 0;

queue<int> q;
q.push(start);
while(!q.empty()){
v = q.front();
q.pop();
visit[v] = 0;//pop后标记为未访问
EDGE* pEdge = dag[v].next;
while(pEdge!=NULL) {
if( (pEdge->flow>0) && (fee[pEdge->id] < fee[v]+pEdge->fee)){
fee[pEdge->id] = fee[v]+pEdge->fee;
pre[pEdge->id] = v;
if(!visit[pEdge->id]){
visit[pEdge->id] = 1;
q.push(pEdge->id);
}
}
pEdge = pEdge -> next;
}
}
}

main(){
int i, j;

scanf("%d %d",&n, &k);

for(i=0;i<n;i++){
for(j=0;j<n;j++){
scanf("%d",&tr[i][j]);
}
}
int ver_num = n*n;
start = 0;
end = 2*ver_num-1;
//构图:change tr[][] to dag[]
EDGE edge[6*N];
int edge_index=0;
for(i=0;i<n;i++){
for(j=0;j<n;j++){
int index_in = i*n + j;
int index_out = index_in+ver_num;
//in
dag[index_in].edge_num = 0;

//对每个in,添加向 out 的边
dag[index_in].edge_num ++;
edge[edge_index].id = index_out;
edge[edge_index].flow = k;
edge[edge_index].fee = tr[i][j];
edge[edge_index].next = NULL;

dag[index_in].next = &edge[edge_index];
edge_index++;
//对每个in,添加向 左的边
if(j>0){
dag[index_in].edge_num ++;
edge[edge_index].id = index_out - 1;
edge[edge_index].flow = 0;
edge[edge_index].fee = 0;
edge[edge_index].next = NULL;

edge[edge_index-1].next = &edge[edge_index];
edge_index++;

}

//对每个in,添加向 上的边
if(i>0){
dag[index_in].edge_num ++;
edge[edge_index].id = index_out - n;
edge[edge_index].flow = 0;
edge[edge_index].fee = 0;
edge[edge_index].next = NULL;

edge[edge_index-1].next = &edge[edge_index];
edge_index++;
}

//out
dag[index_out].edge_num = 0;
dag[index_out].next = NULL;

//对每个out,添加向 in 的边
edge[edge_index].id = index_in;
edge[edge_index].flow = 0;
edge[edge_index].fee = 0;
edge[edge_index].next = NULL;

dag[index_out].edge_num ++;
dag[index_out].next = &edge[edge_index];
edge_index ++;
//对每个out,添加向 右的边
if(j<n-1){
dag[index_out].edge_num ++;

edge[edge_index].id = index_in + 1;
edge[edge_index].flow = k;
edge[edge_index].fee = 0;
edge[edge_index].next = NULL;

edge[edge_index-1].next = &edge[edge_index];

edge_index ++;
}
//对于每个out,添加向 下的边
if(i<n-1){
dag[index_out].edge_num ++;

edge[edge_index].id = index_in + n;
edge[edge_index].flow = k;
edge[edge_index].fee = 0;
edge[edge_index].next = NULL;

edge[edge_index-1].next = &edge[edge_index];

edge_index++;
}
}
}
//find k path
int cur;
while(k){
spfa();
//update flow and fee
cur = end;
i = pre[end];
EDGE *pEdge = NULL;
while(cur!=start){
int tmp=0;

pEdge = dag[i].next;
while(pEdge->id!=cur)
pEdge = pEdge -> next;
(pEdge->flow) --;
if(pEdge->fee != 0){
tmp = pEdge->fee;
sum += (pEdge->fee);
pEdge->fee = 0;
}

pEdge = dag[cur].next;
while(pEdge->id!=i)
pEdge = pEdge -> next;
(pEdge->flow) ++;
pEdge->fee = 0-tmp;

cur = i;
i = pre[i];
}
k--;
}
printf("%d\n",sum);
system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息