2013长沙网络赛J题 Candies(差分约束或数学规律)#by zh

这题真心神坑,刚拿到这个题,想了一会,感觉可以用差分约束做,写完了之后稍微调了一下,交上去WA了,改了一会也还是WA,以为是算法错了。然后发现是有规律的,又改用数学方法去做,写的有些挫,出数据调了不少bug,但是还是WA到死,最后也没做出来。今天又想了一下,队友发现每个点取值的范围是非负,而不是0到10000,不得不吐槽题意真心坑,然后把之前的两个代码都去掉10000的约束以后都过了。读题是硬伤啊……

差分约束:分别维护前i个人拥有石头个数的最大可能值和最小可能值,第i个人可能的最大数值就是dismax[i]-dismin[i-1]

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
#define MAXN 100005
int dismax[MAXN],dismin[MAXN];
int vis[MAXN];
const int INF=0x3f3f3f3f;
struct Node
{
    int v,w;
    Node(int a,int b){v=a,w=b;}
};
int n;
vector<Node> gmin[MAXN],gmax[MAXN];
void spfamax()
{
    for(int i=0;i<=n;i++)
        dismax[i]=INF;
    memset(vis,0,sizeof(vis));
    dismax[0]=0;
    queue<int> q;
    q.push(0);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=false;
        int len=gmax[u].size();
        for(int i=0;i<len;i++)
        {
            int v=gmax[u][i].v,w=gmax[u][i].w;
            if(dismax[v]>dismax[u]+w)
            {
                dismax[v]=dismax[u]+w;
                if(!vis[v])
                {
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
}
void spfamin()
{
    for(int i=0;i<=n;i++)
        dismin[i]=-INF;
    memset(vis,0,sizeof(vis));
    dismin[0]=0;
    queue<int> q;
    q.push(0);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=false;
        int len=gmin[u].size();
        for(int i=0;i<len;i++)
        {
            int v=gmin[u][i].v,w=gmin[u][i].w;
            if(dismin[v]<dismin[u]+w)
            {
                dismin[v]=dismin[u]+w;
                if(!vis[v])
                {
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
}
int main()
{
    //freopen("test.txt","r",stdin);
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<=n;i++)
            gmin[i].clear(),gmax[i].clear();
        for(int i=1;i<=n;i++)
        {
            gmax[i].push_back(Node(i-1,0));
            gmin[i-1].push_back(Node(i,0));
        }
        for(int i=1;i<=n;i++)
        {
            int temp;
            scanf("%d",&temp);
            if(temp!=-1)
            {
                gmax[i-1].push_back(Node(i,temp));
                gmax[i].push_back(Node(i-1,-temp));
                gmin[i-1].push_back(Node(i,temp));
                gmin[i].push_back(Node(i-1,-temp));
            }
        }
        for(int i=1;i<=n;i++)
        {
            int w;
            scanf("%d",&w);
            int u=max(0,i-2),v=min(n,i+1);
            gmax[u].push_back(Node(v,w));
            gmax[v].push_back(Node(u,-w));
            gmin[u].push_back(Node(v,w));
            gmin[v].push_back(Node(u,-w));
        }
        spfamax();
        spfamin();
        int q;
        scanf("%d",&q);
        for(int i=0;i<q;i++)
        {
            int temp;
            scanf("%d",&temp);
            temp++;
            printf("%d\n",dismax[temp]-dismin[temp-1]);
        }
    }
}
数学规律:3,6,9,……都可以算出来,然后第1项可以推4,7,10……,第2项可以推5,8,11……,前两项是固定的,设第一项为x,第二项为num[1]-x,add维护

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define MAXN 100005
int num[MAXN];
int tot[MAXN];
int value[MAXN];
int add[MAXN];
int main()
{
    //freopen("test.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(value,0,sizeof(value));
        memset(add,0,sizeof(add));
        for(int i=1; i<=n; i++)
            scanf("%d",&tot[i]);
        for(int i=1; i<=n; i++)
            scanf("%d",&num[i]);
        int l=0,r=num[1],addx=0,addy=0;
        bool ok=false;
        for(int i=1; i<=n; i++)
        {
            if(i%3==1)
            {
                if(i>3)
                    addx+=num[i-1]-num[i-2];
                if(tot[i]!=-1)
                {
                    value[1]=tot[i]-addx;
                    value[2]=num[1]-value[1];
                    ok=true;
                }
                else
                {
                    //r=min(r,10000-addx);
                    l=max(l,-addx);
                }
                add[i]=addx;
            }
            else if(i%3==2)
            {
                if(i>3)
                    addy+=num[i-1]-num[i-2];
                if(tot[i]!=-1)
                {
                    value[1]=num[1]+addy-tot[i];
                    value[2]=num[1]-value[1];
                    ok=true;
                }
                else
                {
                    r=min(r,num[1]+addy);
                    //l=max(l,num[1]+addy-10000);
                }
                add[i]=addy;
            }
            else
            {
                value[i]=value[i-3]+num[i-1]-num[i-2];
            }
        }
        if((n-2)%3!=0)
        {
            ok=true;
            if((n-2)%3==1)
            {
                value[1]=num[n-1]-num[n]-add[n-2];
                value[2]=num[1]-value[1];
            }
            else
            {
                value[1]=num[1]+add[n-2]-(num[n-1]-num[n]);
                value[2]=num[1]-value[1];
            }
        }
        int q;
        scanf("%d",&q);
        for(int i=0;i<q;i++)
        {
            int temp;
            scanf("%d",&temp);
            if((temp+1)%3==0)
                printf("%d\n",value[temp+1]);
            else if(ok)
            {
                printf("%d\n",value[(temp+1)%3]+add[temp+1]);
            }
            else
            {
                if((temp+1)%3==1)
                    printf("%d\n",r+add[temp+1]);
                else printf("%d\n",num[1]-l+add[temp+1]);
            }
        }
    }
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是贪心算法的分糖果问的报告。 问描述: 有 $n$ 个小朋友和 $m$ 颗糖果,每个小朋友有一个贪心指数 $g_i$,表示他对糖果的喜爱程度。我们需要按照以下要求分配糖果: 1. 每个小朋友至少分到一个糖果。 2. 如果一个小朋友的贪心指数比他旁边的小朋友高,那么他应该得到比旁边的小朋友更多的糖果。 目标是最小化所分发的糖果数量。 解决方案: 首先,我们可以为每个小朋友分配一个糖果。这样,每个小朋友都至少有一个糖果了。 接着,我们需要考虑如何分配剩下的糖果。我们从左到右遍历每个小朋友,如果当前小朋友的贪心指数比前一个小朋友高,那么我们让他的糖果数量比前一个小朋友多一颗。否则,我们只给他一颗糖果。 同样的,我们也要从右到左遍历一遍小朋友,如果当前小朋友的贪心指数比后一个小朋友高,那么我们让他的糖果数量比后一个小朋友多一颗。这样,我们就可以满足所有小朋友的要求了。 最后,我们需要统计一下分配出去的糖果数量。这个数量就是所有小朋友糖果数量之和。 时间复杂度: 对所有小朋友进行两次遍历,所以时间复杂度为 $O(n)$。 代码实现: 下面是 Python 语言的代码实现。 ```python def min_candies(n, g): # 每个小朋友至少分到一个糖果 candies = [1] * n # 左遍历 for i in range(1, n): if g[i] > g[i-1]: candies[i] = candies[i-1] + 1 # 右遍历 for i in range(n-2, -1, -1): if g[i] > g[i+1]: candies[i] = max(candies[i], candies[i+1] + 1) # 统计糖果数量 return sum(candies) ``` 测试样例: 我们可以使用以下样例进行测试。 ```python n = 5 g = [1, 2, 3, 2, 1] print(min_candies(n, g)) # 输出 7 ``` 这个样例中,我们有 $5$ 个小朋友,他们的贪心指数分别为 $[1,2,3,2,1]$。根据贪心算法,我们将分配 $[1,2,3,1,1]$ 颗糖果,总共需要 $7$ 颗糖果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值