<队内胡策> 2017.10.10 (贪心+二分答案+DP)

*56学长们出的题,做的时候觉得好难QAQ,讲完题后自己又打了一遍,开始怀疑起自己的智商T^T。。。好吧就这样吧 _ψ(‥ )


这里写图片描述

注:题目的数据范围以数据范围及提示里的为准。

这里写图片描述


t1:

刚开始做时感觉好像不是很难,因此下决心一定要把这道题做出来!
对于除灰色颜料外的其他颜料,考虑统计一个max,计算一下至少需要多少颜料套装,再对剩余的颜料看看还至少还要多少个颜料套装可以把所需的灰色颜料配出来。
问题的关键就是怎么配。
要知道不同的配法可以决定最后配出灰色颜料的多少。

例如我有4个套装,每个套装有四种颜色,我可以:

这里写图片描述
最后共凑出灰色颜料 200ml 剩余 200 ml;

我也可以:

这里写图片描述
最后共凑出灰色颜料 250ml 剩余50ml;

经过一系列的举例子(?),我们发现如果用来配的单位颜料越少,颜料就越不容易浪费,答案就会更优。(PS:我考试时当然没想到,莫名打了个二分加判断水了40)

既然单位颜料越少越优,反正数据范围小,干脆1ml1ml地配吧!
开一个优先队列,把剩余的颜料统统丢进堆里。每次弹出最多的三种颜料,每种-1,灰色颜料数+1,再丢进去,这样可以保证用的套装更少,答案更优。如果当前弹出的<=0,就把ans++,同时每种颜料数+50ml。

这样还是有些麻烦,不如倒过来想。
每次都把所需的1ml灰色颜料转为当前最少的三种颜料,尽量使每种颜料所需的数量相近,减少浪费。
最后算出所需的颜料套装数。
正解( • ̀ω•́ )✧!

代码:

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

int N,maxn,cnt,ans;
int a[110];
priority_queue<int,vector<int>,greater<int> > q;

int ask()
{
    int b=q.top();
    q.pop();
    return b;
}
int main()
{
    freopen("painter.in","r",stdin);
    freopen("painter.out","w",stdout);
    scanf("%d",&N);
    while(N!=0)
    {
        maxn=0,cnt=0;
        for(int i=1;i<=N;++i)
        {
           scanf("%d",&a[i]);
           q.push(a[i]);
        }
        scanf("%d",&a[N+1]);
        while(a[N+1])
        {
           int x=ask(),y=ask(),z=ask();
           ++x,++y,++z;
           --a[N+1];
           q.push(x),q.push(y),q.push(z);   
        }
        while(!q.empty())
        {
            a[++cnt]=q.top();
            q.pop();
            maxn=max(maxn,a[cnt]);
        }
        ans=maxn/50;
        if(maxn%50!=0) ++ans;
        printf("%d\n",ans);
        scanf("%d",&N);
    }
    return 0;
}

这里写图片描述

t2:

一看到最小距离最大化第一反应二分。因为是在矩阵中求到目标点的距离,所以选择bfs。另外注意矩阵中,两点间的曼哈顿距离就是从一个点到另一点的路径长度。

正解:二分最小距离,bfs判断能否走到目标点,true就调整二分下界,把距离往大处二分,不然就调整上界。因为二分要多次bfs时判断曼哈顿距离,而两点间的距离其实是固定不变的。所以我们先对每个敌人跑一遍bfs,处理出每个点到距离最近的敌人的距离。(并不会TLE)

考试时我是直接暴力判断那个点走或不走,下来又调了调,可以得70分。

ac代码:

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

int N,X,Y,X1,Y1,x2,y2,ans;
int enemy[1010][1010];
int x[4]={0,1,0,-1},
    y[4]={1,0,-1,0};
bool used[1010][1010];
struct maple{
    int x,y,d;
}e[1010],h[1000010];

bool bfs(int n)
{
    memset(used,0,sizeof(used));
    int head=0,tail=1;
    h[tail].x=X1,h[tail].y=Y1;
    h[tail].d=0;
    used[X1][Y1]=1;
    if(enemy[X1][Y1]<n) return false;
    if(enemy[x2][y2]<n) return false;
    while(head<tail)
    {
        ++head;
        for(int i=0;i<=3;++i)
        {
            int xx=h[head].x+x[i],yy=h[head].y+y[i];
            if(xx>=0&&xx<X&&yy>=0&&yy<Y&&!used[xx][yy]&&enemy[xx][yy]>=n)
            {
                used[xx][yy]=1;
                h[++tail]=(maple){xx,yy,h[head].d+1};
                if(xx==x2&&yy==y2) 
                {
                   ans=h[head].d+1;
                   return true;
                }
            }
        }
    }
    return false;
}
void Bfs(int sx,int sy)
{
     int head=0,tail=1;
     memset(used,0,sizeof(used));
     h[tail]=(maple){sx,sy,0};
     used[sx][sy]=1;
     while(head<tail)
     {
        ++head;
        for(int i=0;i<=3;++i)
        {
            int xx=h[head].x+x[i],yy=h[head].y+y[i];
            if(xx>=0&&xx<X&&yy>=0&&yy<Y&&!used[xx][yy])
              if(enemy[xx][yy]>h[head].d+1)
              {
                  enemy[xx][yy]=min(enemy[xx][yy],h[head].d+1);
                  used[xx][yy]=1;
                  h[++tail]=(maple){xx,yy,enemy[xx][yy]};
              }
         }
     }  
}
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);
    memset(enemy,63,sizeof(enemy));
    for(int i=1;i<=N;++i)
    {
       scanf("%d%d",&e[i].x,&e[i].y);
       enemy[e[i].x][e[i].y]=-1;
    }
    for(int i=1;i<=N;++i)
       Bfs(e[i].x,e[i].y);      
    int l=0,r=2010;
    while(l+1<r)
    {
        int mid=(l+r)>>1;
        if(bfs(mid)) l=mid;
        else r=mid; 
    }
    printf("%d %d",l,ans);
    return 0;
}

这里写图片描述

t3:
本来以为是数学题,讲了题以后发现竟然是一个类似背包的dp。。。
因为有负数,所以设天平的初始平衡度为7500(20个重25的砝码挂在一个距离天平中央的距离为15的钩子上, 20* 25 * 15)。
令 dp[i][j]为当前挂上了第i个砝码后天平的平衡度为j时的方案数。
则 dp[i][j]=∑dp[i-1][j-c[k]*w[i]].
最后输出dp[g][7500].

代码:

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

int C,G,maxn1,maxn2;
int c[25],w[25];
int dp[25][15010];

int main()
{
    freopen("balance.in","r",stdin);
    freopen("balance.out","w",stdout);
    scanf("%d%d",&C,&G);
    for(int i=1;i<=C;++i)
        scanf("%d",&c[i]);
    for(int i=1;i<=G;++i)
        scanf("%d",&w[i]);
    dp[0][7500]=1;
    for(int i=1;i<=G;++i)
      for(int k=1;k<=C;++k)
        for(int j=w[i]*c[k];j<=15000;++j)
              dp[i][j]+=dp[i-1][j-w[i]*c[k]];
    printf("%d",dp[G][7500]);
    return 0;
}

ヾノ≧∀≦)o

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值