每日一题 day20 打卡
Analysis
线型动态规划
读入每个人的贪婪度之后,对其按照从大到小的顺序排序,定义状态f[i][j]为前i个人(排序后)分j个饼干的答案,那么答案为f[n][m],考虑状态转移方程。
1、若给第i个人的饼干数大于1 ,那么我们将这i个人的饼干数都减1(总共减n),他们的怨气值是不会改变的,因而这种情况下,f[i][j]=f[i][j-i].
2、若给第i个人的饼干数等于1,那么我们枚举一个k(0≤k<i),表示从k之后一直到i所有的人的饼干数都是1,那么f[i][j]=f[k][j-(i-k)]+k*∑g[c[p]] (k<p<=i).
我们先预处理出g数组的前缀和,即可实现O(n)的转移。
综上,我们在两种决策中取最优即可。另外,本题要求输出方案,我们只需在状态转移时记录每个状态的前驱即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define int long long 6 #define maxn 30+10 7 #define maxm 5000+10 8 using namespace std; 9 inline int read() 10 { 11 int x=0; 12 bool f=1; 13 char c=getchar(); 14 for(; !isdigit(c); c=getchar()) if(c=='-') f=0; 15 for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+c-'0'; 16 if(f) return x; 17 return 0-x; 18 } 19 inline void write(int x) 20 { 21 if(x<0){putchar('-');x=-x;} 22 if(x>9)write(x/10); 23 putchar(x%10+'0'); 24 } 25 int n,m; 26 int g[maxn],num[maxn],sum[maxn],ans[maxn]; 27 int a[maxn][maxm],b[maxn][maxm],dp[maxn][maxm]; 28 inline bool cmp(int x,int y) 29 { 30 return g[x]>g[y]; 31 } 32 inline void fig(int x,int y) 33 { 34 if(!x) return; 35 fig(a[x][y],b[x][y]); 36 if(a[x][y]==x) 37 { 38 for(int i=1;i<=x;i++) ans[num[i]]++; 39 } 40 } 41 signed main() 42 { 43 memset(dp,127,sizeof(dp)); 44 n=read();m=read(); 45 for(int i=1;i<=n;i++) 46 { 47 g[i]=read(); 48 num[i]=i; 49 } 50 sort(num+1,num+n+1,cmp); 51 for(int i=1;i<=n;i++) ans[num[i]]=1; 52 for(int i=1;i<=n;i++) sum[i]=sum[i-1]+g[num[i]]; 53 dp[0][0]=0; 54 for(int i=1;i<=n;i++) 55 { 56 for(int j=i;j<=m;j++) 57 { 58 dp[i][j]=dp[i][j-i]; 59 a[i][j]=i; 60 b[i][j]=j-i; 61 for(int k=0;k<i;k++) 62 { 63 if(dp[i][j]>dp[k][j-(i-k)]+k*(sum[i]-sum[k])) 64 { 65 dp[i][j]=dp[k][j-(i-k)]+k*(sum[i]-sum[k]); 66 a[i][j]=k; 67 b[i][j]=j-(i-k); 68 } 69 } 70 } 71 } 72 write(dp[n][m]); 73 printf(" "); 74 fig(n,m); 75 for(int i=1;i<=n;i++) 76 { 77 write(ans[i]); 78 printf(" "); 79 } 80 return 0; 81 }
请各位大佬斧正(反正我不认识斧正是什么意思)