sdut2414 An interesting game 费用流

An interesting game

Time Limit: 2000ms   Memory limit: 65536K  有疑问?点这里^_^

题目描述

Xiao Ming recently designs a little game, in front of player there are N small hillsides put in order, now Xiao Ming wants to increase some hillsides to block the player, so he prepared another M hillsides, but he does not hope it will be too difficult,so only K in M hillsides are selected to add at most. Paying attention to the original N hillsides, between each two can add only one hillside. Xiao Ming expects players from the starting place to reach the destination in turn and passes all the hillsides to make his distance travelled longest. Please help Xiao Ming how to add the hillsides that he prepared. Note: The distance between two hillsides is the absolute value of their height difference.

输入

The first line of input is T, (1 <= T <= 100) the number of test cases. Each test case starts with three integers N,M,K (2 <= N <= 1000, 1 <= M <= 1000, 1 <= K <= M and 1 <= K < N), which means that the number of original hillsides, the number of hillsides Xiao Ming prepared and The number of most Xiao Ming can choose from he prepared. Then follow two lines, the first line contains N integers Xi (0 <= Xi <= 30), denoting the height of each original hillside, Note: The first integer is player's starting place and the last integer is player's destination. The second line contains M integers Yi (0 <= Yi <= 30), denoting the height of prepared each hillsides.

输出

For every test case, you should output "Case k: " first in a single line, where k indicates the case number and starts from 1. Then print the distance player can travel longest.

示例输入

32 1 16 982 1 16 9153 2 15 9 1521 22

示例输出

Case 1: 3Case 2: 15Case 3: 36

  第三届省赛的一道题,现在已经有N座山,还可以从另外M座里选最多K个插到这些山中间,每两个山中间最多插一个,山之间可以得到的权值是高度差,问从第1座山到最后一座得到的最大权值是多少。

  求最大权值相当于把权值取负数建图求最小费用,难就难在在建图上。这里因为原来N座山是固定的,所以至少获得的权值是这些山高度差的和,我们先把这个记录下来,于是建图只用求新增加的权值。首先起点s连向一个中点mid,容量为K花费为0,保证了最多添加K座山,接下来注意到山的高度最大才30,把高度建成点(只用建30个,而并不是把M个山都建,这样节省了时间),mid和高度点连边,容量为M座山中这个高度山的个数,费用为0。再用N个点表示初始的N座山,把高度的点和2-N的山连起来,容量为1,费用为-(abs(i-a[j])+abs(i-a[j-1])-abs(a[j]-a[j-1]),a[j]为初始第j座山的高度,因为我们只算新增的权值,所以要减去一开始就有的abs(a[j]-a[j-1]),走了这条边就说明往j-1和j中间插了一座高度为i的山,最后把2-N的山和终点连边,容量1,费用0,保证了初始每两座山中间只能插一座新的。

  最后答案是求的最小费用的负数加上初始权值。

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;

 const int MAXN=1100;

int T,N,M,K;
int a[MAXN],cnt[MAXN];

struct Edge{
    int from,to,cap,flow,cost;
};
struct MCMF{
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> G[MAXN];
    int inq[MAXN];  //是否在队列中
    int d[MAXN];    //bellman
    int p[MAXN];    //上一条弧
    int a[MAXN];    //增广量

    void init(int n){
        this->n=n;
        for(int i=0;i<n;i++) G[i].clear();
        edges.clear();
    }
    void add_edge(int from,int to,int cap,int cost){
        edges.push_back((Edge){from,to,cap,0,cost});
        edges.push_back((Edge){to,from,0,0,-cost});
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool bellman(int s,int t,int& flow,int& cost){
        for(int i=0;i<n;i++) d[i]=INF;
        memset(inq,0,sizeof(inq));
        d[s]=0;
        inq[s]=1;
        p[s]=0;
        a[s]=INF;
        queue<int> q;
        q.push(s);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            inq[u]=0;
            int len=G[u].size();
            for(int i=0;i<len;i++){
                Edge& e=edges[G[u][i]];
                if(e.cap>e.flow&&d[e.to]>d[u]+e.cost){
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=G[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to]){
                        q.push(e.to);
                        inq[e.to]=1;
                    }
                }
            }
        }
        if(d[t]==INF) return false;
        if(d[t]>0) return false;
        flow+=a[t];
        cost+=d[t]*a[t];
        int u=t;
        while(u!=s){
            edges[p[u]].flow+=a[t];
            edges[p[u]^1].flow-=a[t];
            u=edges[p[u]].from;
        }
        return true;
    }
    int mincost(int s,int t){
        int flow=0;
        int cost=0;
        while(bellman(s,t,flow,cost));
        return cost;
    }
}g;

int main(){
    freopen("in.txt","r",stdin);
    int cas=0;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&N,&M,&K);
        int ans=0;
        int s=N+31,mid=N+32,t=N+33;
        g.init(N+34);
        for(int i=1;i<=N;i++){
            scanf("%d",&a[i]);
            if(i>1) ans+=abs(a[i]-a[i-1]);
        }
        memset(cnt,0,sizeof(cnt));
        int tmp;
        for(int i=1;i<=M;i++){
            scanf("%d",&tmp);
            cnt[tmp]++;
        }
        g.add_edge(s,mid,K,0);
        for(int i=0;i<=30;i++) if(cnt[i]){
            g.add_edge(mid,i,cnt[i],0);
            for(int j=2;j<=N;j++) g.add_edge(i,30+j,1,-(abs(i-a[j])+abs(i-a[j-1])-abs(a[j]-a[j-1])));
        }
        for(int i=2;i<=N;i++) g.add_edge(30+i,t,1,0);
        printf("Case %d: %d\n",++cas,-g.mincost(s,t)+ans);
    }
    return 0;
}



你可能感兴趣的:(sdut2414 An interesting game 费用流)