有向无环图(DAG,Directed Acyclic Graph)上的动态规划是学习动态规划的基础。很多问题都可以转化为DAG上的最长路、最短路或路径计数问题。
代码:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<algorithm> #include<iostream> using namespace std ; const int MX = 1000 + 10 ; int n ; int G[MX][MX],dp[MX] ; struct node { int x,y ; }T[MX] ; void buildGraph() // 建图 { memset(G,0,sizeof(G)) ; for(int i=0 ;i<n ;i++) for(int j=0 ;j<n ;j++) if(T[i].x>T[j].x&&T[i].y>T[j].y) G[i][j]=1 ; } int DAG(int x) // 记忆化求解 { int& ans = dp[x] ; if(ans > 0) return ans ; ans=1 ; for(int i=0 ;i<n ;i++) if(G[x][i]) { int mx=DAG(i)+1 ; ans = ans > mx ? ans : mx ; } return ans ; } void print(int x) // 打印路径 { printf("%d ",x) ; for(int i=0 ;i<n ;i++) if(G[x][i]&&dp[x]==dp[i]+1) { print(i) ; break ; } } int main() { int Tx ; scanf("%d",&Tx) ; while(Tx--) { scanf("%d",&n) ; for(int i=0 ;i<n ;i++) { scanf("%d%d",&T[i].x,&T[i].y) ; if(T[i].x>T[i].y) swap(T[i].x,T[i].y) ; } int ans=1 ; buildGraph() ; memset(dp,-1,sizeof(dp)) ; for(int i=0 ;i<n ;i++) { int mx=DAG(i) ; ans= mx > ans ? mx : ans ; } for(int i=0 ;i<n ;i++)// 寻找第一个点 if(dp[i]==ans) { printf("%d\n",ans) ; print(i) ; break ; } } return 0 ; }
#include<stdio.h> #include<stdlib.h> #include<iostream> #define INF 1<<30 #define maxn 100+10 using namespace std ; int V[maxn],n; int min[maxn],max[maxn]; inline int Min(int a,int b){return a<b?a:b;} inline int Max(int a,int b){return a>b?a:b;} //打印可行的方案 void print_ans(int* d,int S) { for(int i=1;i<=n;i++) { if(S>=V[i] && d[S]==d[S-V[i]]+1) { printf("%d ",V[i]); print_ans(d,S-V[i]); break; } } } int main() { int S; while(~scanf("%d%d",&S,&n)) //输入面值S和面值的种数n { for(int i=1;i<=n;i++) scanf("%d",&V[i]); max[0]=0; min[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(min[i],min[i-V[j]]+1); max[i]=Max(max[i],max[i-V[j]]+1); } print_ans(min,S); printf(" min\n"); print_ans(max,S); printf(" max\n"); printf("min:%d max:%d\n",min[S],max[S]); } return 0; }
#include<stdio.h> #define N 1100 int v[N],min[N],max[N],min_coins[N],max_coins[N]; void print_ans(int *d,int s, int n) { while(s){ printf("%d ",v[d[s]]); s-=v[d[s]]; } printf("\n"); } int main() { int T,i,j,n,s; scanf("%d",&T); while(T--) { scanf("%d %d",&n,&s); for(i=0;i<n;i++) scanf("%d",&v[i]); min[0]=max[0]=0; for(i=1;i<=s;i++){ min[i]=0x7FFFFFFF; max[i]=-0x7FFFFFFF; } for(i=1;i<=s;i++){ for(j=0;j<n;j++){ if(i>=v[j]){ //min[i]=min[i]<(min[i-v[j]]+1)?min[i]:(min[i-v[j]]+1); if(min[i]>min[i-v[j]]+1){ min[i]=min[i-v[j]]+1; min_coins[i]=j; } if(max[i]<max[i-v[j]]+1){ max[i]=max[i-v[j]]+1; max_coins[i]=j; } //max[i]=max[i]>(max[i-v[j]]+1)?max[i]:(max[i-v[j]]+1); } } } printf("%d %d\n",min[s],max[s]); print_ans(min_coins,s,n); print_ans(max_coins,s,n); } return 0; }
#include<stdio.h> #include<string.h> #define N 1100 int v[N],d[N],vis[N]; int dp(int s, int n) { int i; if(vis[s]) return d[s]; vis[s]=1; d[s]=-1<<30;//没有算过的我们假定很小 for(i=0;i<n;i++) if(s>=v[i]&&d[s]<dp(s-v[i],n)+1) d[s]=dp(s-v[i],n)+1; return d[s]; } void print_ans(int s,int n){ int i; for(i=0;i<n;i++){ if(s>=v[i]&&d[s]==d[s-v[i]]+1){ printf("%d ",v[i]);//输出所选的银币面值 print_ans(s-v[i],n); break; } } } int main() { int T,i,n,s,ans; scanf("%d",&T); while(T--) { scanf("%d %d",&n,&s); for(i=0;i<n;i++) { scanf("%d",&v[i]); } memset(vis,0,sizeof(vis)); vis[0]=1; d[0]=0;//终点状态药初始化为0,访问过 ans=dp(s,n); printf("%d\n",ans); print_ans(s,n); printf("\n"); } return 0; }