HDU 4862 多校联合赛1 最大K路径覆盖

56 篇文章 0 订阅
3 篇文章 0 订阅



以下是题解提供的网络流建图法。。

最小K路径覆盖的模型,用费用流或者KM算法解决,构造二部图,X部有N*M个节点,源点向X部每个节点连一条边,流量1,费用0Y部有N*M个节点,每个节点向汇点连一条边,流量1,费用0,如果X部的节点x可以在一步之内到达Y部的节点y,那么就连边x->y,费用为从x格子到y格子的花费能量减去得到的能量,流量1,再在X部增加一个新的节点,表示可以从任意节点出发K次,源点向其连边,费用0,流量K,这个点向Y部每个点连边,费用0,流量1,最这个图跑最小费用最大流,如果满流就是存在解,反之不存在,最小费用的相反数就是可以获得的最大能量


以下是  某博客 http://blog.csdn.net/u010089558/article/details/38051555  提供的KM算法建图法

题解:讲图变成二分图,X部和Y部的点数都为n×m+k,X部中的i点一步能到达Y部中的j点,则建一条权值为效益-代价,X部中的新添加的K个点与Y中前N*M个点建一条权值为0的点,相应的Y部中后K个点也是一样。这样表示每次遍历的起点的前驱和后继都是新建立的点,并且不会产生任何代价和效益。最后通过判断是否X部中的点都被匹配上就可以判断是否能遍历整个矩阵。最后的最优匹配就是答案。

最后就是求最大匹配。。

KM:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MIN(a,b) ((a)<(b)?(a):(b))
#define INF 999999
#define MAX 400
int n,match[MAX];
bool sx[MAX],sy[MAX];
int lx[MAX],ly[MAX],map[MAX][MAX];
bool path(int u)
{
    sx[u]=true;
    for(int v=0;v<n;v++)
        if(!sy[v]&&lx[u]+ly[v]==map[u][v])
        {
            sy[v]=true;
            if(match[v]==-1||path(match[v]))
            {
                match[v]=u;
                return true;
            }
        }
        return false;
}
//true 是最大匹配 , false是最小匹配
int KM(bool truth)//可以不用更改地处理最小或最大权匹配
{
    int i,j;
    if(!truth)
    {
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                map[i][j]=-map[i][j];
    }
    for(i=0;i<n;i++)
    {
        lx[i]=-INF;
        ly[i]=0;
        for(j=0;j<n;j++)
            if(lx[i]<map[i][j])
                lx[i]=map[i][j];
    }
    memset(match,-1,sizeof(match));
    for(int u=0;u<n;u++)
        while(1)
        {
            memset(sx,0,sizeof(sx));
            memset(sy,0,sizeof(sy));
            if(path(u)) break;
            int dmin=INF;
            for(i=0;i<n;i++)
                if(sx[i])
                    for(j=0;j<n;j++)
                        if(!sy[j])
                            dmin=MIN(lx[i]+ly[j]-map[i][j],dmin);
                        for(i=0;i<n;i++)
                        {
                            if(sx[i])
                                lx[i]-=dmin;
                            if(sy[i])
                                ly[i]+=dmin;
                        }
        }
        int sum=0;
        for(j=0;j<n;j++){
			if(map[match[j]][j]==-INF) 
				return INF;
            sum+=map[match[j]][j];
        }
		if(!truth)
        {
            sum=-sum;
            for(i=0;i<n;i++)
                for(j=0;j<n;j++)
                    map[i][j]=-map[i][j];
        }
        return sum;
}
int N,M,K;
char s[30][30];
#define h1(x,y) (x*M+y)
void init()
{
	n = N*M+K;
    for(int i = 0;i < N;i++)
		scanf("%s",s[i]);
	for(int i = 0;i < n;i++)
		for(int j = 0;j < n;j++)
			map[i][j] = -INF;
	for(int i = 0;i < K;i++) map[N*M+i][N*M+i] = 0;
	for(int i = 0;i < N;i++)
		for(int j = 0;j < M;j++)
		{
			for(int ii = 0;ii < K;ii++) map[N*M+ii][i*M+j] = map[i*M+j][N*M+ii] = 0;
			for(int w = j+1;w < M;w++)
				map[h1(i,j)][h1(i,w)] = (s[i][j]==s[i][w]?s[i][j]-'0':0)-(w-j-1);
			for(int z = i+1;z < N;z++)
					map[h1(i,j)][h1(z,j)] = (s[i][j]==s[z][j]?s[i][j]-'0':0)-(z-i-1);
		}

}

int main()
{	
	int t,tt=0;
	scanf("%d",&t);
	
    while(t--)
    {
		scanf("%d %d %d",&N,&M,&K);
		init();
		int ans = KM(true);
		printf("Case %d : ",++tt);
		if(ans >= INF)
			printf("-1\n");
		else printf("%d\n",ans);
    }
	return 0;
}

以下是费用流

#include <iostream>
#include <algorithm>
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
int sumFlow;
const int MAXN = 502;
const int MAXM = 10002;
const int INF = 1000000000;
struct Edge
{
    int u, v, cap, cost;
    int next;
}edge[MAXM<<2];
int NE;
int head[MAXN], dist[MAXN], pp[MAXN];
bool vis[MAXN];
void init()
{
    NE = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int cap, int cost)
{
    edge[NE].u = u; edge[NE].v = v; edge[NE].cap = cap; edge[NE].cost = cost;
    edge[NE].next = head[u]; head[u] = NE++;
    edge[NE].u = v; edge[NE].v = u; edge[NE].cap = 0; edge[NE].cost = -cost;
    edge[NE].next = head[v]; head[v] = NE++;
}
bool SPFA(int s, int t, int n)
{
    int i, u, v;
    queue <int> qu;
    memset(vis,false,sizeof(vis));
    memset(pp,-1,sizeof(pp));
    for(i = 0; i <= n; i++) dist[i] = INF;
    vis[s] = true; dist[s] = 0;
    qu.push(s);
    while(!qu.empty())
    {
        u = qu.front(); qu.pop(); vis[u] = false;
        for(i = head[u]; i != -1; i = edge[i].next)
        {
            v = edge[i].v;
            if(edge[i].cap && dist[v] > dist[u] + edge[i].cost)
            {
                dist[v] = dist[u] + edge[i].cost;
                pp[v] = i;
                if(!vis[v])
                {
                    qu.push(v);
                    vis[v] = true;
                }
            }
        }
    }
    if(dist[t] == INF) return false;
    return true;
}
int MCMF(int s, int t, int n) // minCostMaxFlow
{
    int flow = 0; // 总流量
    int i, minflow, mincost;
    mincost = 0;
    while(SPFA(s, t, n))
    {
        minflow = INF + 1;
        for(i = pp[t]; i != -1; i = pp[edge[i].u])
            if(edge[i].cap < minflow)
                minflow = edge[i].cap;
        flow += minflow;
        for(i = pp[t]; i != -1; i = pp[edge[i].u])
        {
            edge[i].cap -= minflow;
            edge[i^1].cap += minflow;
        }
        mincost += dist[t] * minflow;
    }
    sumFlow = flow; // 题目需要流量,用于判断
    return mincost;
}

char s[20][20];
int n,m,k;
int hash1(int i,int j){
    return (i-1)*m+j;
}
int hash2(int i,int j){
    return n*m+((i-1)*m+j);
}
int main()
{
    int t,tt=0;
    scanf("%d",&t);
    while(t--){
        init();
        scanf("%d %d %d",&n,&m,&k);
        int S = 0;
        int T = hash2(n,m)+2;
        int np = hash2(n,m)+1;
        for(int i = 1;i <= n;i++)
            scanf("%s",s[i]+1);
        addedge(S,np,k,0);
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= m;j++){
                addedge(S,hash1(i,j),1,0);
                addedge(hash2(i,j),T,1,0);
                addedge(np,hash2(i,j),1,0);
                
                for(int z = j+1;z <= m;z++){
                    int cost = z - j - 1;
                    if(s[i][z] == s[i][j]) cost -= s[i][j]-'0';
                    addedge(hash1(i,j), hash2(i,z), 1, cost);
                }
                for(int k = i+1;k <= n;k++){
                    int cost = k - i - 1;
                    if(s[i][j] == s[k][j]) cost -= s[i][j]-'0';
                    addedge(hash1(i,j),hash2(k,j), 1 , cost);
                }
                
            }
        }
        printf("Case %d : ",++tt);
        int ans=MCMF(S,T,T+1);
        //printf("sumFlow=%d n=%d m=%d\n",sumFlow,n,m);
        if(sumFlow==n*m) cout << -ans << endl;
        else printf("-1\n");
    }

    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值