10.10 高校模拟赛 贪心模拟+BFS+DP

12 篇文章 0 订阅
8 篇文章 0 订阅

出题人

(排名不分先后):

Dewct http://loifrancis.gq/
zzk http://blog.csdn.net/loi__zzk
Cunese 大爷的csdn忘了,,行吧
whales http://blog.csdn.net/cherish_k

Problem 1 painter

题目来源:

http://poj.org/problem?id=2709

题目描述

杂货店出售一种由N(3<=N<=12)种不同颜色的颜料,每种一瓶(50ML),组成的颜料套装。你现在需要使用这N种颜料;不但如此,你还需要一定数量的灰色颜料。杂货店从来不出售灰色颜料——也就是它不属于这N种之一。幸运的是,灰色颜料是比较好配置的,如果你取出三种不同颜色的颜料各x ml,混合起来就可以得到xml的灰色颜料(注意不是3x)。
现在,你知道每种颜料各需要多少ml。你决定买尽可能少的“颜料套装”,来满足你需要的这N+1种颜料。那么你最少需要买多少个套装呢?
输入描述 输入包含若干组测试数据。每组数据一行:第一个数N, 3<=N<=12, 含义如上;接下来N+1个数,分别表示你需要的N+1种颜料的毫升数。最后一种是灰色。所有输入的毫升数<=1000.

注意:输入中不存在每个颜料套装的毫升数。由题意可知,每种各50ml,即一共50N ml
输出描述 每组数据输出一行,最少需要的套装数。
样例输入 3 40 95 21 0
7 25 60 400 250 0 60 0 500
4 90 95 75 95 10
5 0 0 0 0 0 333
0
样例输出 2
8
2
4
数据范围及提示 对于30%的数据 N=3
对于100%的数据3 <= N <= 100
数据最多不超过10组

题解:

先不考虑灰色,满足其他颜料至少需要ans;
每次操作,选取剩余颜料最多的三种,合成1单位灰色颜料,重新sort;
若不足三种且未合成足够的灰色,则ans++,每种颜料加50

代码:

直接sort的std
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int maxx,ans,n,gry;
int b[10000];
void init(){
    maxx=0;
    ans=0;
}
bool cmp(int a,int b){
    return a>b;
}
int main(){
    while(scanf("%d",&n)!=EOF){
        if(n==0) return 0;
        init();
        for(int i=1;i<=n;i++) scanf("%d",&b[i]),maxx=max(maxx,b[i]);
        scanf("%d",&gry);

        ans=maxx/50;
        if(maxx%50) ans++,maxx=ans*50;
        for(int i=1;i<=n;i++) b[i]=maxx-b[i];
        sort(b+1,b+n+1,cmp);
        while(gry>0){
            if(b[3]==0){
                ans++;
                for(int i=1;i<=n;i++) b[i]+=50;
            }
            b[1]--,b[2]--,b[3]--,gry--;
            sort(b+1,b+n+1,cmp);
        }
        printf("%d\n",ans);
    }
}

我的cpp在poj上WA。。。。。随机数据太良心

考场代码需要改正的是:
1.记录每种颜料标号是不必要的,因为颜料的种类是无意义的,我们只关心它们是否不同。
2.没有特判灰色等于0,在剩余颜料不满两种的情况下,ans会+1;
调了一下午啊!!!!!!

考场 堆
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N=150;
int maxx,n,ned[N],ml[N],gry,cnt,ans;

struct node{
    int id,num;
}tmp[5];
priority_queue<node> Q;
bool operator <(node a,node b){
    return a.num<b.num;
}
void done(){
    ans=maxx/50;
    if(maxx%50) ans++,maxx=ans*50;
    for(int i=1;i<=n;i++){
        if(maxx-ned[i])
        Q.push((node){i,maxx-ned[i]});
    }
    if(Q.size()<=2){
        ans++;
        for(int i=1;i<=n;i++) ml[i]=50;
        while(!Q.empty()){
            node u=Q.top();
            Q.pop();
            ml[u.id]+=u.num;
        }
        for(int i=1;i<=n;i++){
            Q.push((node){i,ml[i]});
        }
    }
    while(!Q.empty()){
        for(int i=1;i<=3;i++){
            tmp[i]=Q.top();
            Q.pop();
        }
        cnt+=1;
        if(cnt>=gry) return ;
        for(int i=1;i<=3;i++){
            if(tmp[i].num-1)
            Q.push((node){tmp[i].id,tmp[i].num-1});
        }
        if(Q.size()<=2){
            ans++;
            for(int i=1;i<=n;i++) ml[i]=50;
            while(!Q.empty()){
                node u=Q.top();
                Q.pop();
                ml[u.id]+=u.num;
            }
            for(int i=1;i<=n;i++){
                Q.push((node){i,ml[i]});
            }
        }
    }
    return ;
}
void init(){
    while(!Q.empty()) Q.pop();
    ans=0;
    maxx=0;
    cnt=0;
    for(int i=1;i<=3;i++) tmp[i].id=0,tmp[i].num=0;
}
int main(){
    freopen("painter.in","r",stdin);
    freopen("painter.out","w",stdout);
    while(scanf("%d",&n)!=EOF){
        if(n==0) return 0;
        init();
        for(int i=1;i<=n;i++) {
            scanf("%d",&ned[i]);
            maxx=max(maxx,ned[i]);
        }
        scanf("%d",&gry);
        done();
        printf("%d\n",ans);
    }
    return 0;
}
改 √ 了的堆
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N=150;
int maxx,n,ned[N],ml[N],gry,cnt,ans,tot;

int tmp[5];
priority_queue<int> Q;

void add(){
    ans++;
    tot=0;
    for(int i=1;i<=n;i++) ml[i]=50;
    while(!Q.empty()){
        int u=Q.top();
        Q.pop();
        ml[++tot]+=u;
    }
    for(int i=1;i<=n;i++){
        Q.push(ml[i]);
    }
}
void done(){
    ans=maxx/50;
    if(maxx%50) ans++,maxx=ans*50;

    if(gry==0) return ;//!!!!!!!!!!

    for(int i=1;i<=n;i++){
        if(maxx-ned[i]>0)
        Q.push(maxx-ned[i]);
    }
    if(Q.size()<=2){
        add();
    }
    while(!Q.empty()){
        for(int i=1;i<=3;i++){
            tmp[i]=Q.top();
            Q.pop();
        }
        cnt+=1;
        if(cnt>=gry) return ;
        for(int i=1;i<=3;i++){
            if(tmp[i]-1>0)
            Q.push(tmp[i]-1);
        }
        if(cnt<gry&&Q.size()<=2){
            add();
        }
    }
}
void init(){
    while(!Q.empty()) Q.pop();
    ans=0;
    maxx=0;
    cnt=0;

}
int main(){

    while(scanf("%d",&n)!=EOF){
        if(n==0) return 0;
        init();
        for(int i=1;i<=n;i++) {
            scanf("%d",&ned[i]);
            maxx=max(maxx,ned[i]);
        }
        scanf("%d",&gry);
        done();
        printf("%d\n",ans);
    }
    return 0;
}

Problem 2 escape

来源:

http://hzwer.com/4598.html

题目描述

给出数字N(1<=N<=10000),X(1<=x<=1000),Y(1<=Y<=1000),代表有N个敌人分布一个X行Y列的矩阵上,矩形的行号从0到X-1,列号从0到Y-1再给出四个数字x1,y1,x2,y2,代表你要从点(x1,y1)移到(x2,y2)。在移动的过程中你当然希望离敌人的距离的最小值最大化,现在请求出这个值最大可以为多少,以及在这个前提下,你最少要走多少步才可以回到目标点。注意这里距离的定义为两点的曼哈顿距离,即某两个点的坐标分为(a,b),(c,d),那么它们的距离为|a-c|+|b-d|。
输入描述 第一行给出数字N,X,Y
第二行给出x1,y1,x2,y2
下面将有N行,给出N个敌人所在的坐标

输出描述 在一行内输出你离敌人的距离及在这个距离的限制下,你回到目标点最少要移动多少步。
样例输入 2 5 6
0 0 4 0
2 1
2 3

样例输出 2 14
数据范围及提示
30%: n=1
100%: 1<=n<=1000, 1<=x<=1000, 1<=y<=1000

题解:

预处理出每个点到它最近敌人的距离(把敌人坐标丢进队列里,做bfs),将整张图扫一遍。
二分答案,按照预处理出的距离,判断某点是否能走,bfs;

注意:
二分判断时,首先判断起点是否合法!!

考试的时候,没有预处理,每二分一次重新标记图,成功tle!

代码:

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

struct node{
    int x,y,step;
};
bool used[1005][1005];
int dis[1005][1005]; 
queue<node> Q;
int n,X,Y,x1,x2,y1,y2,ans,a,b;
int dx[10]={0,1,0,-1,0},dy[10]={0,0,1,0,-1};
bool can(int x,int y,int mid){
    if(x>=0&&x<X&&y>=0&&y<Y&&!used[x][y]&&dis[x][y]>=mid) return true;
    return false;
}
void init(int mid){
    memset(used,0,sizeof(used));
    while(!Q.empty()) Q.pop();
    ans=0;
}
bool check(int x){
    init(x);
    Q.push((node){x1,y1,0});
    used[x1][y1]=1;
    if(dis[x1][y1]<x) return false;
    while(!Q.empty()){
        node u=Q.front();
        if(u.x==x2&&u.y==y2){
            ans=u.step;
            return true;
        }
        Q.pop();
        for(int i=1;i<=4;i++){
            int xx=u.x+dx[i],yy=u.y+dy[i];
            if(can(xx,yy,x)){
                used[xx][yy]=1;
                Q.push((node){xx,yy,u.step+1});
            }
        }
    }
    return false;
}
void done(){
    while(!Q.empty()){
        node u=Q.front();
        Q.pop();
        for(int i=1;i<=4;i++){
            int xx=u.x+dx[i],yy=u.y+dy[i];
            if(xx>=0&&xx<X&&yy>=0&&yy<Y&&!used[xx][yy]){
                dis[xx][yy]=u.step+1;
                used[xx][yy]=1;
                Q.push((node){xx,yy,u.step+1});
            }
        }
    }
    return ;
}
int main(){
    freopen("escape.in","r",stdin);
    freopen("escape.out","w",stdout);

    scanf("%d%d%d",&n,&X,&Y);
    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a,&b);
        Q.push((node){a,b,0});
        dis[a][b]=0;
        used[a][b]=1;
    }
    done();
    int l=-1,r=100000000+800;
    while(r-l>1){
        int mid=r+l>>1;
        if(check(mid)) l=mid;
        else r=mid;
    }
    check(l);
    printf("%d %d\n",l,ans);
    return 0;
}

Problem 3 balance

题目来源:

https://vjudge.net/problem/POJ-1837

题目描述

有一个天平,天平左右两边各有若干个钩子,总共有C个钩子,有G个钩码,求将钩码全部挂到钩子上使天平平衡的方法的总数。
输入描述 第一行两个数c, g分别代表钩子数和钩码数
第二行c个数,表示每个钩子距离天平中央的距离c[i],负数表示在左边,正数表示在右边
第三行g个数,表示每个钩码的重量w[i]
输出描述 输出总方案数
样例输入
2 4
-2 3
3 4 5 8

样例输出 2

数据范围及提示 30%: c<=9, g<=9
100%: c<=20, g<=20, -15<=c[i]<=15, w[i]<=25

题解:

一道精彩的初中物理题
设置状态dp[i][j]表示放了i个钩码,平衡度为j的方案数;
平衡度的定义为:右边-左边
由此可知:左边重 j<0,右边重 j>0,平衡 j=0
由数据范围可以推测出,平衡度的绝对值小于7500
为了避免负数作为下标,将平衡度均+7500

枚举钩码
    枚举放在哪个位置
        枚举此时的平衡度
        转移

总结一下,本题主要难在设计状态。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;
int g[25],w[25],dp[25][15020];
int n,m;
int main(){
    freopen("balance.in","r",stdin);
    freopen("balance.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)   scanf("%d",&g[i]);
    for(int i=1;i<=m;i++)   scanf("%d",&w[i]);
    dp[0][7500]=1;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
                for(int k=0;k<=15000;k++){
                    if(k-w[i]*g[j]>=0)
                        dp[i][k]+=dp[i-1][k-w[i]*g[j]];
                }
        }
    }

    printf("%d",dp[m][7500]);
    return 0;
}

result 100+40+0;
T1由于数据水,成绩不准确;

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值