D2 2017.10.10内测

论什么叫做的时候一脸懵逼,一讲题发现并不难2333
T1(题目来源:poj 2709)
这里写图片描述
第一眼感觉不难。
打出来了发现最后一组不对。
咦?输出了7
哪里出了问题?
仔细一想,发现原来可以各种三色的不同原料搭配来得到灰色的,也就是说最后一组数据,不一定非得固定的三种颜料*7才能凑成333,其他的颜色搭配也可以使用,这样有很多浪费,肯定不是最优。
暴力搜索什么的?好像不可取
然后我很zz地发现了一个小规律,对于最后一组的情况,直接333*3/(250*5)+1,行吧,就随便打了打也不知道对不对就交上了,结果得了60.
正解:
贪心。
在凑成灰色颜料时,可以发现,只有每次取的数量越少,凑成的灰色数量才会更多。既然这样,那我们就1ml,1ml地取吧。当然,每次都取剩余数量最多的三种颜料,然后把他们的数量–1,扔进大根堆.虽然是一种很暴力的做法,但是数据范围比较小,可以过
代码:

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

const int maxn=1000+10;
int n,ans,hui;
int num[maxn],b[maxn];

bool cmp(int x,int y)
{
    return x>y;
}
int main()
{
    while(1)
    {
        scanf("%d",&n);
        ans=0;
        if(!n) break;
        memset(b,0,sizeof(b));
        memset(num,0,sizeof(num));
        for(int i=1;i<=n;++i)           scanf("%d",&num[i]);
        scanf("%d",&hui);
        sort(num+1,num+n+1);
        if(num[n]%50==0) ans=num[n]/50;
        else ans=num[n]/50+1;
        for(int i=1;i<=n;++i) b[i]=ans*50-num[i];
        sort(num+1,num+n+1);
        while(hui)
        {
            if(!b[3])
            {
                ans++;
                for(int i=1;i<=n;++i) b[i]+=50;
            }
            else
            {
                hui--;
                b[1]--,b[2]--,b[3]--;
                sort(b+1,b+n+1,cmp);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

T2:(题目来源:bzoj模拟赛)
这里写图片描述
一眼看出来是二分答案加验证。但是验证的时候bfs,要处理一下经过的点到距离他最近的敌人的曼哈顿距离。自己打的时候,是每次bfs都把不能走的点标记为1,结果没调好,也就没交。但是这样的算法肯定超时。
正解:
还是二分答案加验证。只不过预处理每个点到它最近的敌人的曼哈顿距离只跑了一遍dfs。先把所有的敌人扔进队列里,这样保证每个点都被离他最近的敌人更新(最近的当然是1)。(这里说明一下bfs跑出来的最短路实际上就是两个点之间的曼哈顿距离)。在验证答案时,直接O(1)查询就可以了
一共两遍bfs。
代码:

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

const int maxn=1000+10;
int n,X,Y,x1,y1,x2,y2,ans1,ans2,sum;
int dx[5]={0,0,0,1,-1},dy[5]={0,1,-1,0,0};
int ditu[maxn][maxn];
bool vis[maxn][maxn];
struct hh
{
    int x,y;
}e[maxn];
struct lxt
{
    int x,y;
};
struct lxtt
{
    int x,y,tmp;
};
queue<lxt>Q;
queue<lxtt>q;

bool bfs(int x)
{
    while(!q.empty()) q.pop();
    memset(vis,0,sizeof(vis));
    sum=0;
    q.push((lxtt){x1,y1,0});
    vis[x1][y1]=1;
    while(!q.empty())
    {
        int ux=q.front().x,uy=q.front().y,ut=q.front().tmp;
        if(ux==x2&&uy==y2) 
        {
            sum=ut;
            return true;
        }
        q.pop();
        for(int i=1;i<=4;++i)
        {
            int vx=ux+dx[i],vy=uy+dy[i];
            if(vx>=1&&vx<=X&&vy>=1&&vy<=Y&&!vis[vx][vy]&&ditu[vx][vy]>=x)
            {
                q.push((lxtt){vx,vy,ut+1});
                vis[vx][vy]=1;
            }
        }
    }
    return false;
}
void done()
{
    while(!Q.empty())
    {
        int ux=Q.front().x,uy=Q.front().y;
        Q.pop();
        for(int i=1;i<=4;++i)
        {
            int vx=ux+dx[i],vy=uy+dy[i];
            if(vx>=1&&vx<=X&&vy>=1&&vy<=Y&&ditu[vx][vy]==-1)
            {
                ditu[vx][vy]=ditu[ux][uy]+1;
                Q.push((lxt){vx,vy});
            }
        }
    }
}
int main()
{
    memset(ditu,-1,sizeof(ditu));
    scanf("%d%d%d%d%d%d%d",&n,&X,&Y,&x1,&y1,&x2,&y2);
    x1++,x2++,y1++,y2++;
    for(int i=1;i<=n;++i) 
    {
        scanf("%d%d",&e[i].x,&e[i].y);
        e[i].x++,e[i].y++;
        ditu[e[i].x][e[i].y]=0;
        Q.push((lxt){e[i].x,e[i].y});
    }
    done();
    int l=0,r=min(ditu[x1][y1],ditu[x2][y2]);
    while(l+1<r)
    {
        int mid=(l+r)>>1;
        if(bfs(mid)) 
        {
            l=mid;
            ans1=l;
            ans2=sum;
        }
        else r=mid;
    }
    if(bfs(r)) ans1=r;
    else bfs(l),ans1=l;
    ans2=sum;
    printf("%d %d",ans1,ans2);
    return 0;
}

T3(题目来源:poj 1837)
这里写图片描述
这是一道dp
记dp[i][j] 表示放到第i个钩码是,能得到平衡度为j的方案数。
平衡度的定义:由于数组不能有负数下标,我们通过数据范围可计算得:如果只挂一边,倾斜程度最大时,所有钩码的重量乘以距离的和最大是7500.所以我们定义平衡度为7500时,天平是平衡的。当向右边倾斜时,平衡度大于7500且小于15000,向左边倾斜时,平衡度小于7500且大于1。
于是我们有转移方程:
dp[i][j+g[i]*c[k]]+=dp[i-1][j]
枚举k为第i个钩码放的位置
代码:

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

int C,G;
int c[30],g[30],dp[30][15010];

int main()
{
    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",&g[i]);
    dp[0][7500]=1;
    for(int i=1;i<=G;++i)
      for(int j=0;j<=15000;++j)
        if(dp[i-1][j])
          for(int k=1;k<=C;++k)
            dp[i][j+g[i]*c[k]]+=dp[i-1][j];
    printf("%d",dp[G][7500]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值