第一次给学弟学妹讲题,紧张到语无伦次。
好不容易看到两个学妹感觉要被我劝退了。
半个小时讲完图论基础快到不可思议,当场裂开。
我后知后觉发现自己在念经(bushi)
啊啊啊啊啊啊我不要讲题啦啊啊啊啊啊。
好的现在我发现两个学妹走了,真就当场劝退了555555
(以下是课上提到的例题
hdu2363 Cycling
题意:给定一个n个点m条边的图,图中每个点都有一个高度h,
求在保证路径中的结点的最大和最小高度之差最小的情况下,从1到n的最小高度差和最短路。
思路:带限制的最短路 枚举路径中的最大高度和最小高度,跑最短路。
(可以先把所有的最大最小高度的组合根据高度差从小到大排序,从高度差小的开始枚举
在求最短路的过程中,如果结点的高度h不在枚举的范围内,就跳过。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int maxn=105; const int maxm=10005; const int inf=2e9; int n,num,to[maxm],nxt[maxm],last[maxn],vis[maxn]; int w[maxm],dis[maxn],h[maxn]; struct hi { int dh,l,h; }a[5005]; bool cmp(hi x,hi y){return x.dh<y.dh;} struct node { int num; int dis; node(int x,int y) { num=x;dis=y; } bool operator < (const node &a)const{return dis>a.dis;} //重载运算符< }; priority_queue<node>q; void add(int x,int y,int z) { to[++num]=y; nxt[num]=last[x]; last[x]=num; w[num]=z; } int dij(int low,int high,int s,int t) { int i,x,y; for (i=1;i<=n;i++) { dis[i]=inf;vis[i]=0; } dis[s]=0; q.push(node(s,0)); while (!q.empty()) { x=q.top().num; q.pop(); if (vis[x]) continue; if (x==t) return dis[t]; vis[x]=1; for (i=last[x];i!=-1;i=nxt[i]) { y=to[i]; if (h[y]<low || h[y]>high) continue; if (dis[x]+w[i]<dis[y]) { dis[y]=dis[x]+w[i]; q.push(node(y,dis[y])); } } } return dis[t]; } int main() { int i,j,m,T,x,y,z,re,tmp,ans,dist,cnt; int low,high,lim_low,lim_high; scanf("%d",&T); while (T--) { num=0; memset(last,-1,sizeof(last)); scanf("%d%d",&n,&m); for (i=1;i<=n;i++) scanf("%d",&h[i]); while (m--) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } lim_low=min(h[1],h[n]); lim_high=max(h[1],h[n]); ans=0; dist=0; cnt=0; for (i=1;i<=n;i++) for (j=i+1;j<=n;j++) if (h[i]>h[j]) { a[++cnt].dh=h[i]-h[j]; a[cnt].l=h[j]; a[cnt].h=h[i]; } else { a[++cnt].dh=h[j]-h[i]; a[cnt].l=h[i]; a[cnt].h=h[j]; } sort(a+1,a+cnt+1,cmp); for (i=1;i<=cnt;i++) { low=a[i].l; high=a[i].h; tmp=high-low; if (lim_low<low || high<lim_high) continue; if (ans!=0 && tmp>ans) break; re=dij(low,high,1,n); if (re!=inf) { if (ans==0 || re<dist) { ans=tmp; dist=re; } } } printf("%d %d ",ans,dist); } return 0; }
poj3013 Big Christmas Tree
题意:给一张图,图中的每个点和每条边都有对应权值。
要求构造一棵以1为根的生成树,使得所有边的代价之和最小。 定义边的代价=边权 * 边的所有后代节点的点权之和 0 ≤n,m≤ 50000
思路: 虽然题目中提到构造生成树,但这其实是一题最短路。
仔细一想,∑(边权*子树点权和)=∑(点权*点到根路径上的边权和)。 所以就变成了单源最短路问题。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; typedef long long ll; const ll inf=1e18; const int maxn=50005; int n,num,to[maxn<<1],nxt[maxn<<1],last[maxn],vis[maxn]; ll w[maxn<<1],dis[maxn],val[maxn]; struct node { int num; ll dis; node(int x,ll y) { num=x;dis=y; } bool operator < (const node &a)const{return dis>a.dis;} //重载运算符< }; priority_queue<node>q; void add(int x,int y,ll z) { to[++num]=y; nxt[num]=last[x]; last[x]=num; w[num]=z; } void dij(int s) { int i,x,y; for (i=1;i<=n;i++) { dis[i]=inf;vis[i]=0; } dis[s]=0; q.push(node(s,0)); while (!q.empty()) { x=q.top().num; q.pop(); if (vis[x]) continue; vis[x]=1; for (i=last[x];i!=-1;i=nxt[i]) { y=to[i]; if (dis[x]+w[i]<dis[y]) { dis[y]=dis[x]+w[i]; q.push(node(y,dis[y])); } } } } int main() { int T,m,i,x,y,flag; ll ans,z; scanf("%d",&T); while (T--) { num=0; memset(last,-1,sizeof(last)); scanf("%d%d",&n,&m); for (i=1;i<=n;i++) scanf("%d",&val[i]); while (m--) { scanf("%d%d%lld",&x,&y,&z); add(x,y,z); add(y,x,z); } dij(1); ans=0; flag=0; for (i=1;i<=n;i++) if (dis[i]==inf) { flag=1; break; } else ans+=dis[i]*val[i]; if (!flag) printf("%lld ",ans); else printf("No Answer "); } return 0; }