当前位置: 代码迷 >> 综合 >> UVa 12563 Jin Ge Jin Qu hao(DP 多种状态表示)
  详细解决方案

UVa 12563 Jin Ge Jin Qu hao(DP 多种状态表示)

热度:38   发布时间:2023-09-23 09:34:42.0

状态A

状态:d[i][j] 已经第i首歌还剩j时间,还能最多唱几首

决策:唱或不唱 1)d[i][j]-->d[i+1][j-song[i]] ;
               2)d[i][j]-->d[i+1][j] ;

选择:最大的但j>0的 

int dp(int i,int j)
{int& ans=d[i][j];if(d[i][j]>=0) return ans;ans=-1;if(i==n) ans=0;else{if(j>song[i]) ans=dp(i+1,j-song[i])+1;ans=max(ans,dp(i+1,j));}return ans;
}int main()
{int T,kase=0;scanf("%d",&T);while(T--){scanf("%d%d",&n,&t);for(int i=0;i<n;i++)scanf("%d",&song[i]);memset(d,-1,sizeof(d)); int ans=dp(0,t);int ans_t;for(int j=1;j<=t;j++) if(d[n][j]!=-1) {   //能走到n点的且t为最小的 ans_t=j;break;}printf("Case %d: %d %d\n",++kase,ans+1,t-ans_t+Jin);}return 0;
} 
2)递推法

int main()
{int T,kase=0;scanf("%d",&T);while(T--){scanf("%d%d",&n,&t);for(int i=0;i<n;i++)scanf("%d",&song[i]);memset(d,-1,sizeof(d)); int ans_t;for(int i=n;i>=0;i--)for(int j=0;j<=t;j++){int& ans=d[i][j];if(i==n) ans=0;else{ans=-1;if(j>song[i]) ans=d[i+1][j-song[i]]+1;ans=max(ans,d[i+1][j]);}}
//		for(int i=n;i>=0;i--)  //测试输出d[i][j]的值
//		{
//			for(int j=0;j<=t;j++)
//			cout<<d[i][j]<<' ';	
//			cout<<endl;	
//		}printf("Case %d: %d %d\n",++kase,a[0][t]+1,t-ans_t+Jin);}//难打印时间,因为d[0][60~100]都为1; return 0;
} 


状态B

状态:d[i][j] 已经第i首歌用时j时间,还能最多唱几首
决策:唱或不唱 1)d[i][j]-->d[i+1][j+song[i]] ;
                             2)d[i][j]-->d[i+1][j] ;
选择:最大的但j<t的 

1)记忆化搜索法

int dp(int i,int j)
{int& ans=d[i][j];if(d[i][j]>=0) return ans;ans=-1;if(i==n) ans=0;else{if(j+song[i]<t) ans=dp(i+1,j+song[i])+1;ans=max(ans,dp(i+1,j));}return ans;
}int main()
{int T,kase=0;scanf("%d",&T);while(T--){scanf("%d%d",&n,&t);for(int i=0;i<n;i++)scanf("%d",&song[i]);memset(d,-1,sizeof(d)); int ans=dp(0,0);int ans_t;for(int j=t-1;j>=0;j--) if(d[n][j]!=-1) {ans_t=j;break;}printf("Case %d: %d %d\n",++kase,ans+1,ans_t+Jin);}return 0;
} 

2)递推法

int main()
{int T,kase=0;scanf("%d",&T);while(T--){scanf("%d%d",&n,&t);for(int i=0;i<n;i++)scanf("%d",&song[i]);memset(d,-1,sizeof(d)); int ans_t;for(int i=n;i>=0;i--)for(int j=0;j<=t;j++){int& ans=d[i][j];if(i==n) ans=0;else{ans=-1;if(j+song[i]<t) ans=d[i+1][j+song[i]]+1;ans=max(ans,d[i+1][j]);}}
//		for(int i=n;i>=0;i--)
//		{
//			for(int j=0;j<=t;j++)
//			cout<<d[i][j]<<' ';	
//			cout<<endl;	
//		}//难打印时间,因为d[0][0~40]都为1; 
//		printf("Case %d: %d %d\n",++kase,ans+1,ans_t+Jin);}return 0;
} 

状态C

状态:d[i][j] 从第i首歌起,用了j时间,最多能唱几首 
决策:唱或不唱 1)d[i][j]-->d[i+1][j+song[i]] ;
               2)d[i][j]-->d[i+1][j] ;
选择:最大的但j<t的 

1)记忆化搜索

int dp(int i,int j)
{int& ans=d[i][j];if(d[i][j]>=0) return ans;ans=-1;if(i==n+1) ans=0;else{if(j+song[i]<t) ans=dp(i+1,j+song[i])+1;ans=max(ans,dp(i+1,j));}return ans;
}
int main()
{int T,kase=0;scanf("%d",&T);while(T--){scanf("%d%d",&n,&t);for(int i=1;i<=n;i++)scanf("%d",&song[i]);memset(d,-1,sizeof(d)); int ans=dp(1,0),ans_t;for(int i=t-1;i>=0;i--)if(d[n+1][i]!=-1) {ans_t=i;break;}printf("Case %d: %d %d\n",++kase,ans+1,ans_t+Jin);}return 0;
}

2)递推法

int main()
{int T,kase=0;scanf("%d",&T);while(T--){scanf("%d%d",&n,&t);for(int i=1;i<=n;i++)scanf("%d",&song[i]);memset(d,-1,sizeof(d)); for(int i=n+1;i>=0;i--)for(int j=0;j<=t;j++){int& ans=d[i][j];if(i==n+1) ans=0;else{ans=-1;if(j+song[i]<t) ans=d[i+1][j+song[i]]+1;ans=max(ans,d[i+1][j]);}}
//		for(int i=n;i>=1;i--)
//		{
//			for(int j=0;j<=t;j++)
//			cout<<d[i][j]<<' ';	
//			cout<<endl;	
//		}
//		printf("Case %d: %d %d\n",++kase,ans+1,ans_t+Jin);}return 0;
} 


状态C

状态:d[i][j] 从第i首歌起,j时间内,最多能唱几首 
决策:唱或不唱 1)d[i][j]-->d[i+1][j-song[i]] ;
               2)d[i][j]-->d[i+1][j] ;
选择:最大的但j>0的 

1)记忆化搜索

int dp(int i,int j)
{int& ans=d[i][j];if(d[i][j]>=0) return ans;ans=-1;if(i==n+1) ans=0;else{if(j-song[i]>0) ans=dp(i+1,j-song[i])+1;ans=max(ans,dp(i+1,j));}return ans;
}
int main()
{int T,kase=0;scanf("%d",&T);while(T--){scanf("%d%d",&n,&t);for(int i=1;i<=n;i++)scanf("%d",&song[i]);memset(d,-1,sizeof(d)); int ans=dp(1,t),ans_t;for(int i=0;i<t;i++)if(d[n+1][i]!=-1) {ans_t=i;break;}printf("Case %d: %d %d\n",++kase,ans+1,t-ans_t+Jin);}return 0;
} 

2)递推法

int main()
{int T,kase=0;scanf("%d",&T);while(T--){scanf("%d%d",&n,&t);for(int i=1;i<=n;i++)scanf("%d",&song[i]);memset(d,-1,sizeof(d)); for(int i=n+1;i>=0;i--)for(int j=0;j<=t;j++){int& ans=d[i][j];if(i==n+1) ans=0;else{ans=-1;if(j-song[i]>0) ans=d[i+1][j-song[i]]+1;ans=max(ans,d[i+1][j]);}}
//		for(int i=n;i>=1;i--)
//		{
//			for(int j=0;j<=t;j++)
//			cout<<d[i][j]<<' ';	
//			cout<<endl;	
//		}
//		printf("Case %d: %d %d\n",++kase,ans+1,ans_t+Jin);}return 0;
} 

下面是相反的定义,从1到n

状态:d{i][j] 前i首歌,用时j最多唱了几首

状态转移:1) d[i][j]-->d[i-1][i-song[i]]+1

                    2) d[i][j]-->d[i-1][j];

状态选择:最大的

int main()
{int T,kase=0;scanf("%d",&T);while(T--){scanf("%d%d",&n,&t);for(int i=1;i<=n;i++)scanf("%d",&song[i]);memset(d,-1,sizeof(d)); for(int i=0;i<=n;i++) for(int j=0;j<=t;j++){int& ans=d[i][j];if(i==0) ans=0;else{ans=-1;if(j-song[i]>0) ans=d[i-1][j-song[i]]+1;ans=max(ans,d[i-1][j]);        		}}cout<<d[n][t]; 
//		printf("Case %d: %d %d\n",++kase,ans+1,ans_t+Jin);}return 0;
} 

但递推法的都不知道怎么求出最大用时,可能是状态定义的不好