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;
}



### SDUT PTA 编程题解与在线评测系统使用说明 #### 关于SDUT PTA在线评测系统的概述 SDUT PTA(Programming Teaching Assistant)是一个面向学生学习编程的在线评测平台,主要用于支持山东理工大学计算机科学及相关专业的课程教学。该平台提供了丰富的练习题目以及自动化的测试机制,帮助学生巩固所学的知识并提升实际编程能力[^1]。 #### 如何使用SDUT PTA在线评测系统? 以下是关于如何有效利用SDUT PTA进行编程训练的关键要点: 1. **注册账号** 学生需通过学校指定的方式完成账户注册,并登录到对应的班级页面参与作业提交和考试活动。 2. **熟悉界面布局** 登录后可以看到左侧导航栏包含了“我的作业”、“公开试题”等功能模块;右侧则显示具体任务列表及其状态信息(未做/已交/得分情况)。点击某道题目可以查看其详细描述、样例输入输出以及其他提示内容[^2]。 3. **编写代码并调试** 对于像“计算1到n的和”的简单问题,可以直接采用如下C语言实现方式来解决: ```c #include<stdio.h> int main(){ int n, sum=0; scanf("%d",&n); for(int i=1;i<=n;i++) { sum +=i; } printf("%d\n",sum); return 0; } ``` 此段代码实现了基本的功能需求——读取用户输入的一个正整数值`n`,并通过循环累加得到最终的结果输出。 4. **提交解决方案** 完成本地开发环境中的编码工作之后,在网页端找到对应位置上传源文件或者直接粘贴文本框内的代码片段即可发起评测请求。注意遵循每道题目的特殊约束条件比如时间复杂度限制等规定。 5. **分析反馈结果** 提交完成后会迅速获得运行状况报告,如果存在错误,则应仔细阅读报错消息重新审视自己的逻辑是否存在漏洞再尝试修正直至完全正确为止。 #### 常见问题解答 对于初学者来说可能会遇到一些困惑之处,下面列举几个典型场景供参考: - 如果发现即使按照标准答案复制也得不到满分怎么办?可能是因为忽略了某些边界情形处理或者是格式化方面的小失误所致。 - 当面对较复杂的算法类挑战不知道从哪里下手时建议先梳理清楚思路画程图辅助理解后再动手实践。 - 还有就是保持耐心多试几次不要轻易放弃因为每一次失败都是成长的机会! ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值