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];
}