有向无环图(DAG,Directed Acyclic Graph)
- 嵌套矩形 链接
最长路及其字典序
如何求DAG中不固定起点的最长路径呢
设d(i)表示从节点i出发的最长路长度,转移方程: d(i) = max{d(j)+1|(i,j)∈E }int dp(int i) { int& ans=d[i];//为表项d[i]声明一个引用ans,这样任何对ans的读写实际上都是在对d[i]进行 if(ans>0) return ans; ans=1; for(int j=1;j<=n;j++) { if(G[i][j]) ans=ans>(dp(j)+1)?ans:dp(j)+1; } return ans; } //为了是字典序最小 void print_ans(int i) { printf("%d ",i); for(int j=1;j<=n;j++) if(G[i][j]&&d[i]==d[j]+1) { print_ans(j); break; } }
- 硬币问题
固定终点的最长路和最短路
d(i) 的确切含义变为“从节点i出发到节点0的最长路径长度”//求最长路的代码---------下面代码有些问题 int dp(int S) { int& ans=d[S]; if(ans>=0) return ans; ans=0; for(int i=1;i<=n;i++) if(S>=V[i]) ans=ans>(dp(S-V[i])+1)?ans:(dp(S-V[i])+1); return ans; } //修改后---方法1 int dp(int S) { int& ans=d[S]; if(ans!=-1) return ans; ans=-1<<30; for(int i=1;i<=n;i++) if(S>=V[i]) ans=max(ans,dp(S-V[i])+1); return ans; } //方法2 int dp(int S) { if(vis[S]) return d[S];//vis[i]表示状态i是否被访问过 vis[S]=1; int& ans=d[S]; ans=-1<<30; for(int i=1;i<=n;i++) if(S>=V[i]) ans=max(ans,dp(S-V[i])+1); return ans; }
求最小,最大的两个值
min[0]=max[0]=0; for(int i=1;i<=S;i++) { min[i]=INF;max[i]=-INF; } for(int i=1;i<=S;i++) for(int j=1;j<=n;j++) if(i>=V[j]) { min[i]=min[i]<min[i-V[j]]+1?min[i]:min[i-V[j]]+1; max[i]=max[i]>max[i-V[j]]+1?max[i]:max[i-V[j]]+1; } printf("%d %d\n",min[S],max[S]);
如何输出字典序最小的方案呢?
void print_ans(int* d,int S) { for(int i=1;i<=n;i++) if(S>=V[i]&&d[S]==d[S-V[i]]+1z) { printf("%d ",i); print_ans(d,S-V[i]); break; } }
不少人喜欢用另外一种打印方法:递推时直接用min_coin[S]记录满足min[S]==min[S-V[j]]+1的最小的i,则打印省去print_ans函数中的循环,并方便地把递归写成迭代
for(int i=1;i<=S;i++) for(int j=1;j<=n;j++) if(i>=V[j]) { //注意判断中用的是“>"和”<",原因在于“字典序最小解”要求当min/max值相同时取最小的i值。 //反过来,如果j是从大到小枚举的,就需要把“>"和“<"改成”>="和“<="才能求出字典序最小解 if(min[i]>min[i-V[j]]+1) { min[i]=min[i-V[j]]+1; min_coin[i]=j; } if(max[i]<max[i-V[j]]+1) { max[i]=max[i-V[j]]+1; max_coin[i]=j; } } //只需调用print_ans(min_coin,S)和print_ans(max_coin,S)即可 void print_ans(int* d,int S) { while(S) { printf("%d ",d[S]); S-=V[d[S]]; } }