题意:从m门课选出n个排到n天,每天一门,难度须递增,每门课对应着一个作业量Xi,且Xi = Xi-1 + k or Xi - Xi-1 * k,总作业量要尽可能大,问能否排布,若能排布,求方案。
思路:比赛时企图用DFS暴搜,无奈超时,尝试各种优化都没成功,赛后看了题解才知道是DP,同时也发现很多时候能用搜索解决的题目如果用DP会省下很多时间!
建立一个三维DP数组,dp[i][j][k],i表示第i天,j表示第i天时排了第j门课,k表示选择的作业量为第j门课作业区间的左端点+k。需要注意的地方是还应建立pre数组来标记DP路径。
代码如下:
#include<iostream> #include<algorithm> #include<cstring> #include<vector> using namespace std; struct subject{ long long a,b; int c,id; bool operator<(subject& t){ return c<t.c; } }arr[60]; struct route{ int addition,id; route(){} route(int x,int y):id(x),addition(y){} }pre[60][60][120],ending; int n,m,k; long long dp[60][60][120];//day/num/work bool makeDp(){ memset(dp,-1,sizeof(dp)); for(int j=1;j<=m;j++) for(long long i=arr[j].a;i<=arr[j].b;i++){ dp[1][j][i-arr[j].a]=i; } for(int i=1;i<n;i++){ for(int j=i;j<=m&&m-j>=n-i;j++){ for(long long kk=arr[j].a;kk<=arr[j].b;kk++){ if(dp[i][j][kk-arr[j].a]==-1) continue; for(int jj=j+1;jj<=m;jj++){ if(arr[jj].c==arr[j].c) continue; if(k*kk>=arr[jj].a&&k*kk<=arr[jj].b){ if(dp[i+1][jj][k*kk-arr[jj].a]<dp[i][j][kk-arr[j].a]+k*kk){ dp[i+1][jj][k*kk-arr[jj].a]=dp[i][j][kk-arr[j].a]+k*kk; pre[i+1][jj][k*kk-arr[jj].a]=route(j,kk-arr[j].a); } } if(k+kk>=arr[jj].a&&k+kk<=arr[jj].b){ if(dp[i+1][jj][k+kk-arr[jj].a]<dp[i][j][kk-arr[j].a]+k+kk){ dp[i+1][jj][k+kk-arr[jj].a]=dp[i][j][kk-arr[j].a]+k+kk; pre[i+1][jj][k+kk-arr[jj].a]=route(j,kk-arr[j].a); } } } } } } long long flag=-1; for(int i=n;i<=m;i++){ for(long long j=arr[i].a;j<=arr[i].b;j++){ if(flag<dp[n][i][j-arr[i].a]){ flag=dp[n][i][j-arr[i].a]; ending=route(i,j-arr[i].a); } } } if(flag!=-1) return true; return false; } void findAns(bool x){ if(!x) cout<<"NO"; else { cout<<"YES\n"; vector<route> ans; route ha=ending; for(int i=n;i>=1;i--){ ans.push_back(ha); ha=pre[i][ha.id][ha.addition]; } for(int i=n-1;i>=0;i--){ cout<<arr[ans[i].id].id<<" "<<arr[ans[i].id].a+ans[i].addition<<endl; } } } int main(){ cin>>n>>m>>k; for(int i=1;i<=m;i++){ cin>>arr[i].a>>arr[i].b>>arr[i].c; arr[i].id=i; } sort(arr+1,arr+1+m); findAns(makeDp()); return 0; }