动态规划测试3(test20170406)

17 篇文章 0 订阅
本文通过三道编程题目,介绍了动态规划在解决实际问题中的应用。包括操练(Training)问题,求解最大操练人数的子矩阵;炸弹(Bomb)问题,寻找最大连续子序列和;战争(War)问题,找到最高战斗力的团队。文章提供问题描述、输入输出样例、数据范围,并给出了解题思路和关键代码实现。
摘要由CSDN通过智能技术生成

前言

突然就考试,做完1,2题就开始浪了。于是就第三题打了个暴力…
用Emacs之后代码缩进似乎就呵呵了…

操练(Training.pas/c/cpp Time:1s Memory:256M

【问题描述】

高老大有一个 NN 的广场,被均分成 NN 个格子。其中某些格子种上了树。
为了维护世界的和平,为了贯彻和平与发展的原则,老大不得不开始操练部下了。部下们必须站在广场中某没种上树个格子中,而且一个格子内不允许有两个人站着。同时,为了显得整齐划一,部下们要维护一个方阵的阵型,也就是N*N的广场内的一个 XY 的矩形(该矩形的长和宽必须与广场的长和宽平行),每个格子上都必须有一个部下。现在老大想知道,一次最多操练多少个部下?

【输入】

输入文件名为Training.in。
输入第一行两个正整数N和M,代表广场的长和宽。
下接一个N行M列的字符矩阵,若第I行第J列为‘0’则代表该格子上有一颗树。

【输出】

输出文件名为Training.out。
输出一次最多能够操练的部下个数。

【输入输出样例】

Training.in
2
11
11
Training.out
4

【数据范围】

对于80%的数据, N250
对于100%的数据, N1000

【题解】

这道题目就是极大化子矩形 的一个模板吧,所以还好做吧。
这个题目的母题是USACO中的rectbarn,里面讲的很详细。
解释一下我这里 f[i][j] 表示的是这个点前面1的个数,那么你应该就看的懂了吧。。。

【代码】

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))

const int size = 1000+10;
int n,ans;
int f[size][size];
char ch[size][size];
int minn,cnt;

inline int read() {
    int f=1,in=0;
    char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())
    if(ch=='-')
        f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())
    in=in*10+ch-'0';
    return in*f;
}

int main() {
    freopen("training.in","r",stdin);
    freopen("training.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)
    scanf("%s",ch[i]+1);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        if(ch[i][j]=='1') f[i][j]=f[i][j-1]+1;
    /*for(int i=1;i<=n;i++) {
    for(int j=1;j<=n;j++) 
        printf("%d ",f[i][j]);
    puts("");
    }*/
    for(int i=n;i>=1;i--)
    for(int j=1;j<=n;j++) {
        minn=f[j][i];
        cnt=1;
        if(minn*(n-j+1)<=ans) continue;
        ans=Max(ans,cnt*minn);
        for(int k=j+1;k<=n;k++) {
        minn=Min(minn,f[k][i]);
        cnt++;
        if(minn*(n-j+1)<=ans) break;
        ans=Max(ans,cnt*minn);
        }
    }
    printf("%d\n",ans);
    return 0;
}   

炸弹(Bomb.pas/c/cpp Time:1s Memory:256M)

【问题描述】

高老大又在鼓捣炸弹了,这不,启动了一个了。英明英勇的老大又在跟他的部下们表演空手光速拆炸弹了。
炸弹上是一个圆盘,圆盘上顺时针写着N个数。其实这个炸弹就是要求从中选择若干个连续的数(注意每个数最多只能选一次)加起来,使得这些数字的和最大,然后输入这个
最大的和,计时就会停止。现在你被老大要求上台表演,时间是1s,任务就交给你了。

【输入】

输入文件名为Bomb.in。
输入第一行包含一个正整数N,表示数字的个数。
第二行包含N个整数,为所给的数字。

【输出】

输出文件名为Bomb.out。
输出包含一个整数,为最大的可以得到的和。

【输入输出样例】

Bomb.in
8
2 -4 6 -1 -4 8 -1 3
Bomb.out
14

【数据范围】

对于30%的数据 1N200 ;
对于70%的数据 1N10000 ;
对于100%的数据 1N100000 , 答案在longint范围内。

【题解】

这个题目可以很明显的发现就是求最大连续子序列的题目
然后我们可以一般轻松的求出在非环上的最大连续子序列, O(n) 出界
然后就是处理此时在环上的情况了。。。
我们将一个方案分成两部分,一部分从 1 开始递增,一部分从 n 开始递减。
然后用dp就可以求出末端不超过 i 且从 1 或 n 出发的最大连续子序列。
然后就没有然后了…

【代码】

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
typedef int LL;
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))

const int size = 200000+10;
LL n,ans;
LL a[size],f[size];

inline LL read() {
    LL f=1,in=0;
    char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())
    if(ch=='-')
        f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())
    in=in*10+ch-'0';
    return in*f;
}

inline LL erfen(LL l,LL r) {
    if(l==r) return f[l];
    LL mid=(l+r)>>1;
    return Min(erfen(l,mid),erfen(mid+1,r));
}

int main() {
    freopen("bomb.in","r",stdin);
    freopen("bomb.out","w",stdout);
    n=read();
    for(LL i=1;i<=n;i++)
    a[i]=a[i+n]=read();
    LL m=n<<1;
    for(LL i=1;i<=m;i++)
    f[i]=f[i-1]+a[i];
    LL minn=0;
    for(LL i=n;i<=m;i++) {
    if(f[i-n-1]==minn) minn=erfen(i-n,i);
    else minn=Min(minn,f[i]);
    ans=Max(ans,f[i]-minn);
    }
    printf("%d\n",ans);
    return 0;
}

战争(War.pas/c/cpp Time:1s Memory:256M)

【问题描述】

高老大要打团战了。他要召集N(N是奇数)个人去组织一场战争。现在高老大的手下有T个人,每个人都有一个战斗力值和影响力值。当这N个人的影响力值之和超过M的时候,必然会引起巨大的社会动荡。为了爱与和平,老大明智地决定,不引起巨大的社会动荡。同时,英明的老大发现,一个团队整体能力等于这N个人的战斗力的中位数,中位数越高则战斗力越强。现在老大想知道,这N个人的团队整体能力最大为多少。

【输入】

输入文件名为War.in。
输入第一行为三个正整数N,T,M,意义如上述。
后接T行,每行两个正整数Wi和Vi,代表每个人的战斗力值和影响力值。

【输出】

输出文件名为War.out。
输出一行一个整数,代表这N个人的团队最大的整体能力。无解输出-1。

【输入输出样例】

War.in

3 5 70
30 25
50 21
20 20
5 18
35 30

War.out
35

【样例解释】

选第2、4、5个人,影响力21+18+30=69<=70,同时该团队的整体能力为最高的35。

【数据范围】

对于30%的数据,保证 T200
对于100%的数据,保证 T100000 N20000 每个人的影响力值 100000 M2311 .

【题解】

我当时一看这个题目,然后就开始准备打暴力,不怎么想去想,然后打完暴力30分,再然后就…没有然后了…
考后据说就是把每个人按照战斗力值进行排序,那么枚举中位数,对于第 i 个人,我们只需要求出 1i1 中影响力值最小的 n2 个人之和, i+1n 中影响力值最大的 n2 个人之和。
若它们之和再加上当前人的影响力值小于等于 m,就可以更新最大值。
在这之后就得要求区间前K大的数的和了。
只要建一个大根堆,每次比较当前数字与堆顶元素的大小关系,若比堆顶元素大,则不管;若比堆顶元素小,则取出堆顶元素,放入当前数字,然后更新和。
时间复杂度 O(nLogn)

【代码】

先纪念我的30分暴力代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxx = 200+10;
int n,t,m,ans=0;
bool yes;
int a[maxx],b[maxx],num[maxx];
struct people {
    int c,w;
}ren[maxx];

bool cmp(const people &x,const people &y) {
    return x.c<y.c;
}

void dfs(int k,int sum) {
    int l=t-(n-k);
    for(int i=a[k-1]+1;i<=l;i++)
    if(sum+ren[i].w<=m) {
        a[k]=i;
        b[k]=ren[i].c;
        if(k==n) {
        yes=true;
        if(k&1)
            ans=max(ans,b[(k+1)/2]*2);
        else
            ans=max(ans,b[k/2]+b[k/2+1]);
        }
        else
        dfs(k+1,sum+ren[i].w);
        a[k]=0;b[k]=0;
    }
}

int main() {
    freopen("war.in","r",stdin);
    freopen("war.out","w",stdout);
    scanf("%d%d%d",&n,&t,&m);
    if(t>25)
    return 0;
    for(int i=1;i<=t;i++)
    scanf("%d%d",&ren[i].c,&ren[i].w);
    sort(ren+1,ren+1+t,cmp);
    if(t<=25)
    dfs(1,0);
    if(!yes)
    puts("-1");
    else {
      if(ans&1)
      printf("%.1lf\n",(double)ans/2.0);
      else
      printf("%d\n",ans/2);
    }
    return 0;
}

然后这是AC代码

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;

inline int read() {
    int in=0,f=1;
    char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())
    if(ch=='-')
        f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())
    in=in*10+ch-'0';
    return in*f;
}

const int size = 100000+10;

struct Node {
    int w,c,num;

    bool operator > (const Node &A) const {
    return A.c<c;
    }
    bool operator < (const Node &A) const {
    return A.c>c;
    }
}node[size];

inline bool cmp(const Node &a,const Node &b) {
    return a.w<b.w;
}

int n,t;
LL m;
bool use[size];

priority_queue<Node ,vector<Node>,greater<Node> > q1;
priority_queue<Node ,vector<Node>,greater<Node> > q2;
priority_queue<Node ,vector<Node>,less<Node> > q3;

int main() {
    freopen("war.in","r",stdin);
    freopen("war.out","w",stdout);
    n=read();t=read();m=read();
    for(int i=1;i<=t;i++)
    node[i].w=read(),node[i].c=read();
    sort(node+1,node+t+1,cmp);

    int k=n>>1;
    LL sum1=0,sum2=0;
    Node Top;
    for(int i=1;i<=t;i++)
    node[i].num=i;
    for(int i=t-k-1;i>=1;i--)
    q1.push(node[i]);
    for(int i=t;i>t-k;i--)
    q2.push(node[i]);
    for(int i=1;i<=k;i++) {
    Top=q1.top();q1.pop();
    sum1+=Top.c;
    use[Top.num]=true;
    }
    for(int i=1;i<=k;i++) {
    Top=q2.top();
    q2.pop();
    sum2+=Top.c;
    q3.push(Top);
    }
    for(int i=t-k;i>k;i--) {
    if(use[i]) {
        sum1-=node[i].c;
        Top=q1.top();
        while(use[Top.num]) {
        q1.pop();
        Top=q1.top();
        }
        sum1+=Top.c;
        use[Top.num]=true;
        q1.pop();
    }
    else use[i]=true;
    if(sum1+sum2+node[i].c<=m) {
        printf("%d\n",node[i].w);
        return 0;
    }
    Top=q3.top();q3.pop();
    sum2-=Top.c;q2.push(Top);
    q2.push(node[i]);
    Top=q2.top();q2.pop();
    sum2+=Top.c;q3.push(Top);
    }
    puts("-1");
    return 0;
}

总结

还得继续想,dp水平才上的去啊,暴力少打点,多想些吧
最后%%%Yzy大神%%% 即将AK

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值