国庆做题报告(一)
来源:互联网 发布: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];}
阅读全文
0 0
- 国庆做题报告(一)
- 国庆一
- 国庆回家做农活
- 130712做题报告
- 国庆假期微信大数据报告
- 国庆天涯一游
- 国庆上海一行(一)
- 我们要为国庆做点事情
- 国庆将至,教你一招过国庆
- 国庆
- 国庆~
- 国庆
- 国庆
- 国庆
- 国庆
- 国庆
- 国庆
- 国庆
- MATLAB中使用libsvm
- Dungeon Master bfs
- 红黑树插入
- 关于matlab中求定积分int和quad命令的使用
- 上传和引用以编译的module
- 国庆做题报告(一)
- OGG-01756 创建抽取进程有警告 文档 ID 1330577.1
- 职业规划
- opencv学习笔记第五章 使用形态学滤波对图像进行开闭运算
- [BZOJ3930][CQOI2015]选数(数论+容斥)
- linux下的find文件查找命令与grep文件内容查找命令
- IDEA 重复代码快速重构
- EventBus总结
- Java 并发编程 之 volatile(三)