国庆做题报告(一)

1. 过路费(cost)

题目:
这里写图片描述
数据范围

       对于30%的数据,N<=10,M<=20,Q<=5。   
       对于60%的数据,N<=200,M<=4000,Q<=100。
       对于100%的数据,N<=300,M<=40000,Q<=100000,1<=ci<=100000,1<=z<=1000。

这道题总共应该分为两种方法,SPFA与floyd,但是我们可看到N明显比M小很多,所以floyd比SPFA快很多,但是如果不是这个数据,或许spfa就更好了

SPFA

首先,我们发现如果我们用以往的单纯的最短路是不行的,因为存在过路费这个东西,所以有可能你走的路最短,但是所要的过路费却高到爆炸,所以,我们要怎么做呢?因为我们发现如果我们能够限定住路上的最高过路费的话,这个问题就迎刃而解了,那么我们就以这个为突破口进行SPFA,对所有过路费小于自身的孩子进行遍历,这样,我们就保持了最多的过路费在我们手上了,对所有节点遍历后,对于每个询问,我们就都可以直接输出了。
代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=305;
const int maxm=40005;
int read()
{
    char ch=getchar();
    int sum=0,f=1;
    while(ch<'0'||ch>'9') 
    {
        if(ch=='-') f
        =-1; 
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') 
    sum=sum*10+ch-'0',ch=getchar();
    return sum*f;
} //读入优化 
int h[maxn],next[maxm<<1],to[maxm<<1],val[maxm<<1],cnt; //双向图记得开两倍 
void add(int u,int v,int w)
{
    next[++cnt]=h[u];
    h[u]=cnt;
    to[cnt]=v;
    val[cnt]=w;
}
int cost[maxn],ans[maxn][maxn],n,m;
int dis[maxn],vis[maxn];
void SPFA(int s)
{
    memset(vis,0,sizeof vis);
    memset(dis,0x3f,sizeof dis);
    //每次清空vis和初始化dis 
    queue<int>q; //重新定义q 
    q.push(s);dis[s]=0;vis[s]=1;
    while(!q.empty()) 
    {
        int u=q.front(); q.pop();
        vis[u]=0;
        for(int i=h[u];i;i=next[i]) 
        {
            int v=to[i],w=val[i];
            if(cost[v]>cost[s]) continue;
            if(dis[v]>dis[u]+w) 
            {
                dis[v]=dis[u]+w;
                if(!vis[v]) q.push(v),vis[v]=1;
            }
        }
    }

    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      ans[i][j]=min(ans[i][j],dis[i]+dis[j]+cost[s]);
    //看当前点是否可以更新某两点的答案 
}
int main()
{
    int u,v,w;
    n=read(),m=read();
    for(int i=1;i<=n;i++)cost[i]=read();
    for(int i=1;i<=m;i++) 
    {
        u=read();v=read();w=read();
        add(u,v,w),add(v,u,w); //双向图 
    }

    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++) 
      if(i!=j) ans[i][j]=INF; //初始化找最小 

    for(int i=1;i<=n;i++) SPFA(i); //枚举点 

    int q=read();
    for(int i=1;i<=q;i++)
    {
        u=read(),v=read();
        printf("%d\n",ans[u][v]);
    }
    return 0;
}

Floyd

在此题中floye是个时间复杂度很低的算法,因为N的数据很小,所以O(n^3)的方法so easy,但我们需要调用两个数组,一个存路径长度,一个存总值,但是,这时我们发现,存不了当前过路费最大值啊,不着急,在预处理里面我们可以首先对所有的过路费从小到大排序,这样,我们所处理的路径中的最大路费必定在i,j,k中,求它们的max值,就可以了。
代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,t=0,h[500],q;
int cost[400];
long long f[500][500],ff[500][500];
struct node
{
  int j,zhi;
}e[1000];
bool cmp(struct node a,struct node b)
{
    return a.zhi<b.zhi;
}//排序 
int bb[400];
int main()
{
   // freopen("cost1.in","r",stdin);
 // freopen("cost.out","w",stdout);
    memset(f,19999,sizeof(f));
    memset(ff,19999,sizeof(ff));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&e[i].zhi);
        e[i].j=i;//因为待会要排序所以记住它的下标;
    }
    sort(e+1,e+n+1,cmp);
   for(int i=1;i<=n;i++)bb[e[i].j]=i;//存下标;
    for(int i=1;i<=m;i++)
    {
       int a,b;
       long long c;
       scanf("%d%d%lld",&a,&b,&c);
       a=bb[a];b=bb[b];//因为现在的a已经不是之前的a了,所以就要用到我们之前存的下标; 
       ff[a][b]=ff[b][a]=min(c,ff[a][b]); //数据有重边;(这个是两点距离); 
       f[a][b]=f[b][a]=ff[a][b]+max(e[a].zhi,e[b].zhi);//这个是最终耗费值; 
    }
    for(int i=1;i<=n;++i)f[i][i]=ff[i][i]=0;//自己到自己为0,(似乎不要也可以) 
    for(int k=1;k<=n;k++)
      for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
      {    
                ff[i][j]=min(ff[i][j],ff[i][k]+ff[k][j]);
                f[i][j]=min(f[i][j],ff[i][j]+max(e[k].zhi,max(e[i].zhi,e[j].zhi)));//同时更新两个值; 
      }
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        int a,b;
        cin>>a>>b;
        a=bb[a];b=bb[b];//同上; 
        cout<<f[a][b]<<endl;//直接输出,舒服! 
    }
    return 0;
}

3.上课(class)

题目:
这里写图片描述

思路

这是一道裸的增广路算法(滑稽),好吧其实就是一个DP,我们其实不难发现,有些课程中间其实是存在时间间隔的,那么,在那个时间里面,我们就可以放肆的做我们能做的最快的作业(因为作业有无限,比我们国庆作业还多),这样只要对做作业时间进行预处理,然后再把各个课程按开始时间排序,通过计算一些不重合课程的课程中间的时间可以做的作业总和,这样,我们就可以方便的求出我们所需要的答案了;
代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int t,m,n;
int mn[105];
int f[100005];
struct node
{
    int q,c,neng;
}ke[1005];
struct nod
{
    int n,s;
}zu[100009];
int cmp(const node &a,const node &b)
{
    if(a.q<b.q)return 1;
    return 0;
}
int main()
{
  //freopen("class.in","r",stdin);
  //freopen("class.out","w",stdout);
  scanf("%d",&t);
  scanf("%d",&m);
  scanf("%d",&n);
  for(int i=0;i<=101;i++)mn[i]=10000000;//这个数组代表能力值为i时做一份作业耗时最短的时间 
  for(int i=1;i<=m;i++)
    cin>>ke[i].q>>ke[i].c>>ke[i].neng;
  for(int i=1;i<=n;i++)
    cin>>zu[i].s>>zu[i].n,mn[zu[i].n]=min(mn[zu[i].n],zu[i].s);//更新存最小值; 
  sort(ke+1,ke+m+1,cmp);
  for(int i=1;i<=100;i++)mn[i]=min(mn[i],mn[i-1]);//因为能力值为i时可以做小于等于它的所有作业; 
  ke[0].c=1; 
  ke[0].q=0;
  ke[m+1].q=t+1;
  ke[0].neng=1;//因为处理1时要用0,而最后的值存在f[m+1]里,所以两个都要处理一下; 
  for(int i=1;i<=m+1;i++)
  for(int j=0;j<i;j++)
  {
       if(ke[j].q+ke[j].c>ke[i].q)continue;//如果两个课程有重合就跳过; 
       f[i]=max(f[i],f[j]+(ke[i].q-ke[j].q-ke[j].c)/mn[ke[j].neng]);//否则计算在间隔时间内所能写的作业; 
  }
  cout<<f[m+1];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值