国庆做题报告(一)

来源:互联网 发布:ewsa软件字典手机号码 编辑:程序博客网 时间:2024/05/02 05:06

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];}
原创粉丝点击