2018/7/8-纪中某C组题【jzoj1619,jzoj1620,jzoj1621,jzoj1622】

前言

分数 250 250 ,十分开心


正题


T1:音乐节拍

洛谷题目链接:https://www.luogu.org/problemnew/show/P2969

大意

有n段音乐,每段音乐持续时间不同,q个询问求一个时间点再放那首歌

考试时

开始时发现询问的时间点不是按顺序来的,于是就想到了离线算法。

解题思路

先将询问排个序,然后一个指针指向现在的音乐,如果不是的话就往前推。

代码

#include
#include
#define MN 50000
using namespace std;
struct node{
    int num,que,ans;
}qu[MN+1];
int n,q,b[MN+1];
bool cmp(node x,node y)
{
    return x.quebool cmp2(node x,node y)
{
    return x.numint main()
{
    //freopen("mnotes.in","r",stdin);
    //freopen("mnotes.out","w",stdout);
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++)
      scanf("%d",&b[i]);
    for (int i=1;i<=q;i++)
    {
        scanf("%d",&qu[i].que);
        qu[i].num=i;//标记
    }
    sort(qu+1,qu+1+q,cmp);//排序
    int j=1,now=b[1]-1;//记录现在位置
    for (int i=1;i<=q;i++)
    {
        while (qu[i].que>now)//往前推
        {
            j++;
            now+=b[j];
        }
        qu[i].ans=j;//记录
    }
    sort(qu+1,qu+1+q,cmp2);//排回原来的顺序
    for (int i=1;i<=q;i++)
      printf("%d\n",qu[i].ans);//输出
}

T2:电视游戏问题

大意

洛谷链接:https://www.luogu.org/problemnew/show/P2967
有n个价格不同平台,上面有不同的游戏,每个游戏有不同的价格和价值,要求价格不超过v时价值最大。

考试时

开始就想到了,然后以为会超时就没打

解题思路

f[0][i] f [ 0 ] [ i ] 表示到现在并且买了这个平台用了i元时最大价值
f[1][i] f [ 1 ] [ i ] 表示到上次用了i元时最大价值
要将上一次继承过来

f[0][i]=f[1][iw] f [ 0 ] [ i ] = f [ 1 ] [ i − w ]

然后动态转移

f[0][i]=max{f[0][iw]+c} f [ 0 ] [ i ] = m a x { f [ 0 ] [ i − w ] + c }

最后更新 f[1] f [ 1 ]

f[1][j]=max{f[0][j]  ,  f[1][j]} f [ 1 ] [ j ] = m a x { f [ 0 ] [ j ]     ,     f [ 1 ] [ j ] }

代码

#include
#include
using namespace std;
int n,v,f[51][100001],w,c,k,wc;
int main()
{
    freopen("vidgame.in","r",stdin);
    freopen("vidgame.out","w",stdout);
    scanf("%d%d",&n,&v);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d",&w,&k);
        for (int j=w;j<=v;j++)
          f[0][j]=f[1][j-w];//继承上次
        for (int m=1;m<=k;m++)
        {
            scanf("%d%d",&wc,&c);
            for (int j=v;j>=w+wc;j--)
              f[0][j]=max(f[0][j],f[0][j-wc]+c);
              //动态转移
        }
        for (int j=1;j<=v;j++)
          f[1][j]=max(f[0][j],f[1][j]),f[0][j]=0;
          //更新最大
    }
    printf("%d",f[1][v]);
}

T3:头晕的奶牛

洛谷链接:https://www.luogu.org/problemnew/show/P2017

大意

一个n个点的图,有m1条有向边,有m2条无向边,将无向边变为有向边使得图变为有向无环图。

考试时

有向无环图,我就想到了拓扑排序,然后就敲了一个东西发现不行。然后我就研究,发现可以将所有的点都集结在终点是出度为0的。然后打了一个奇怪的东西就80。

解题思路

先用有向边建图,然后再进行拓扑排序,之后一条无向边的话就将拓扑序排在前面的连排在后面的(因为拓扑序排在后面的无论如何也回不到前面)。

代码

#include
#include
using namespace std;
struct node{
    int to,next;
}a[300001];
int n,m1,m2,x,y,ls[100001],in[100001],tot,head,tail,state[100001];
int d[100001],s,t,star,ta;
void addl(int x,int y)
{
    a[++tot].to=y;in[y]++;
    a[tot].next=ls[x];ls[x]=tot;
}
void bfs()//拓扑排序
{
    do
    {
        x=state[++head];
        for (int i=ls[x];i;i=a[i].next)
        {
            y=a[i].to;
            if (!d[y])
            {
                in[y]--;
                if (in[y]==0)
                {
                    state[++tail]=y;
                    d[y]=++ta;
                }
            }
        }
    }
    while (headint main()
{
    //freopen("dizzy.in","r",stdin);
    //freopen("dizzy.out","w",stdout);
    scanf("%d%d%d",&n,&m1,&m2);
    for (int i=1;i<=m1;i++)
    {
        scanf("%d%d",&x,&y);
        addl(x,y);
    }
    for  (s=1;s<=n;s++)
      if (in[s]==0) state[++tail]=s,d[s]=++ta;//寻找起点
    bfs(),t++;
    for (int i=1;i<=m2;i++)
    {
        scanf("%d%d",&x,&y);
        if (d[x]y]) printf("%d %d\n",x,y);//判断
        else printf("%d %d\n",y,x);
    }
}

T4:过路费

洛谷:https://www.luogu.org/problemnew/show/P2966

大意

一个图,一个点到另一个点的距离就是路径边权和加上路径上的最大点值。

考试时

写了一个spfa,然后发现一种情况
2018/7/8-纪中某C组题【jzoj1619,jzoj1620,jzoj1621,jzoj1622】_第1张图片
这时spfa就会走 1>2>4>5 1 − > 2 − > 4 − > 5 代价11,可是如果走 1>3>4>5 1 − > 3 − > 4 − > 5 的话代是价10。然后我就想到了一个方法:
f[i][j] f [ i ] [ j ] 表示到达i点时路径上最大点值的是j时的最短距离。
然后超时70分

解题思路

首先我们将点值进行排序用 num[i] n u m [ i ] 表示点权从小到大排在第 i i 的点。然后枚举 k k 时我们先从点权最小的点开始枚举,然后

dis[i][j]=min{dis[i][k]+dis[k][j]} d i s [ i ] [ j ] = m i n { d i s [ i ] [ k ] + d i s [ k ] [ j ] }

cost[i][j]=min{dis[i][j]+max{c[i],c[j],c[k]}} c o s t [ i ] [ j ] = m i n { d i s [ i ] [ j ] + m a x { c [ i ] , c [ j ] , c [ k ] } }

排点权的目的是为了保证 max{c[i],c[j],c[k]} m a x { c [ i ] , c [ j ] , c [ k ] } 中有里面最大的

代码

#include
#include
#include
#define maxs(x,y,z) max(x,max(y,z))
using namespace std;
int n,m,k,num[251],dis[251][251],cost[251][251],c[251],x,y,w;
int main()
{
    memset(dis,127/3,sizeof(dis));
    memset(cost,127/3,sizeof(cost));
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;i++)
    {
        num[i]=i;
        scanf("%d",&c[i]);
    }
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&w);
        dis[x][y]=dis[y][x]=min(dis[x][y],w);
        //无向图
    }
    for (int i=1;ifor (int j=i+1;j<=n;j++)
        if (c[num[i]]>c[num[j]])
          swap(num[i],num[j]);//排序
    for (int q=1;q<=n;q++)
    {
        int k=num[q];
        for (int i=1;i<=n;i++)
          for (int j=1;j<=n;j++)
            if (i!=j&&i!=k&&j!=k)
            {
               dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
               cost[i][j]=min(cost[i][j],dis[i][j]+maxs(c[i],c[j],c[k]));
               //Floyd
            }
    }
    for (int i=1;i<=k;i++)
    {
        scanf("%d%d",&x,&y);
        printf("%d\n",cost[x][y]);
    }
}

你可能感兴趣的:(图论,模拟,dp,模拟赛,广搜)