noj 2112 拯救活动室的男女比例(最大费用最大流)

拯救活动室的男女比例

时间限制(普通/Java) :  2000 MS/ 6000 MS          运行内存限制 : 81920 KByte
总提交 : 116            测试通过 : 38 

比赛描述

    为了拯救活动室的男女比例,队长kalili带着队员们连他一起共k人去说服妹子们努力学习算法加入A协大家庭,妹子们站成一个直角三角形,一共m行,第i行有i个妹子,每个妹子有一个颜值aij,规定每轮只能一个人来说服妹子且一人只能说服一轮,开始都面对第一个妹子,每说服完一个妹子只能往前或者往左走继续去说服别的妹子,如果遇到已经被说服的妹子则不用说服继续走,每个妹子最多被说服一次,现在已知A协的男精英们口才很好,不管颜值多高的妹子都能直接说服,求他们能说服的妹子们的总颜值最大是多少




输入

第一行一个整数T代表共T组样例,每组样例第一行包含两个数字m和k,接下来m行,第i行有i个数字表示对应妹子的颜值

(1 <= T <= 200,1 <= m <= 25, 0 <= k <= m,0 <= aij <= 108)

T >= 100的数据不超过五组

输出

对于每组样例,输出一个数字表示能说服的妹子们的最大总颜值


样例输入

2
2 1
1
1 2
3 2
1
2 3
4 5 6

样例输出

4
21

提示

对于第二组样例

第一个人可以以(1,1) -> (2,1) -> (2, 2) -> (3,2) -> (3,3)的顺序说服5个妹子

第二个人的顺序可以是(1, 1) -> (2, 1) -> (3, 1),因为(1,1) (2,1)位置的妹子已经被说服了,他这一轮只说服了一个妹子。

两轮下来所有妹子都被说服,因此总颜值就是她们的颜值和为21


思路:很明显的网络流,就是不知道会不会TLE,试了一发TLE,然后发现N开小了,改大之后就AC了

只要跟题目所说的一样能达到就连边即可,注意一个点要拆成两个点,为了让费用只能加一次,拆成两个点后连两条边,一条容量为1,费用为aij,一条容量为k,费用为0,由于是最大费用最大流,所以如果会经过这个店,一定会先流容量为1的边

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
#define N 1350
#define INF 999999999
struct Edge
{
    int u,v,next,cap,cost;
} edge[N*N];
int cnt,head[N];
int vis[N],pp[N];
long long d[N];
void init()
{
    cnt=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].cap=cap;
    edge[cnt].cost=cost;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].u=v;
    edge[cnt].v=u;
    edge[cnt].cap=0;
    edge[cnt].cost=-cost;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
int spfa(int s,int t,int n)
{
    queue<int>q;
    memset(vis,0,sizeof(vis));
    memset(pp,-1,sizeof(pp));///pp[i]表示最短路径上以i为终点的边的编号
    for(int i=0; i<=n; i++)
        d[i]=-INF;
    d[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].v;
            if(edge[i].cap>0&&d[v]<d[u]+edge[i].cost)
            {
                d[v]=d[u]+edge[i].cost;
                pp[v]=i;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    if(d[t]==-INF) return 0;///找不到一条到终点的路
    return 1;
}
long long MCMF(int s,int t,int n)
{
    long long mincost=0;
    int minflow;///最大费用,路径中最小流量,总流量
    while(spfa(s,t,n))///找当前的最长路
    {
        minflow=INF+1;
        for(int i=pp[t]; i!=-1; i=pp[edge[i].u])
            minflow=min(minflow,edge[i].cap);///从路径中找最小的流量
        for(int i=pp[t]; i!=-1; i=pp[edge[i].u])
        {
            edge[i].cap-=minflow;///当前边减去最小流量
            edge[i^1].cap+=minflow;///反向边加上最小流量
        }
        mincost+=d[t]*minflow;///最小费用等于路径和*每条路径的流量(经过多少次)
    }
    return mincost;
}
int main()
{
    int n,m,k,v,T,tot=1;
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%d %d",&m,&k);
        int s=0,n=2*m*m+1;
        addedge(s,1,k,0);
        for(int i=1; i<=m; i++)
        {
            for(int j=1; j<=i; j++)
            {
                scanf("%d",&v);
                addedge((i-1)*m+j,m*m+(i-1)*m+j,1,v);
                addedge((i-1)*m+j,m*m+(i-1)*m+j,k,0);
                if(i!=m) addedge(m*m+(i-1)*m+j,i*m+j,k,0);
                if(j!=i) addedge(m*m+(i-1)*m+j,(i-1)*m+j+1,k,0);
            }
        }
        addedge(2*m*m,n,k,0);
        printf("%I64d\n",MCMF(s,n,n));
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值