$CH5105\ Cookies$ 线性$DP+$贪心

CH

 

是很有趣的一道题 : )

 

Sol

第一反应就是f[i][j]表示前i个小朋友分j块饼干的最小怨气值

但是一个孩子所产生的怨气值并不固定,它与其他孩子获得饼干的情况有关

 

这里可以用到一个贪心,就是贪婪度大的孩子应该获得尽量多的饼干

所以先按照贪婪度由大到小排序,那么获得的饼干数也会是非严格单调递减的

然而发现很还是难转移,因为这样直接转移需要前一个孩子获得的饼干数与比前一个孩子获得饼干多的孩子数

在现有的DP状态下,很难高效维护这两种信息

 

对状态做一个等价转化:

1.若第i个孩子获得的饼干数大于1

可以将所有的孩子获得的饼干同时减去1,它们的相对值并没有改变,所以这样所得到的答案任然是正确的

$f[i][j]=f[i][j-i]$

2.若第i个孩子获得的饼干数等于1

就要枚举k(1<=k<=i)表示在i前获得饼干数为1的第一个孩子是谁

$f[i][j]=min(f[k][j-(i-k+1)+(k-1)*\sum_{t=k}^{i}g[t])$

 

最后,这题还要记录答案

在DP中需要给出方案时,一般的做法是额外使用一些与DP状态大小相同的数组记录下来每个状态的最优解是从何处转移而来的.最终,在DP求出最优解后,通过一次递归,沿着记录的每一步回到初态,即可得到一条从初态到最优解的转移路径,也就是所求的具体方案.

这题的答案统计与一般的题目不太一样,要细心.

 

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<vector>
 5 #include<algorithm>
 6 #define Rg register
 7 #define il inline
 8 #define db double
 9 #define ll long long
10 #define inf 2100000000
11 #define go(i,a,b) for(Rg int i=a;i<=b;++i)
12 #define yes(i,a,b) for(Rg int i=a;i>=b;--i)
13 using namespace std;
14 il int read()
15 {
16     int x=0,y=1;char c=getchar();
17     while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
18     while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
19     return x*y;
20 }
21 struct node{int dat,pos;}g[31];
22 struct node1{int i,j;}a[31][5001];
23 il bool cmp(node x,node y){return x.dat>y.dat;}
24 int n,m,ans[31],f[31][5001];
25 int main()
26 {
27     n=read(),m=read();
28     go(i,0,n)go(j,0,m)f[i][j]=inf;f[0][0]=0;
29     go(i,1,n)g[i].dat=read(),g[i].pos=i;
30     sort(g+1,g+n+1,cmp);
31     go(i,2,n)g[i].dat+=g[i-1].dat;
32     go(i,1,n)
33         go(j,i,m)
34     {
35         f[i][j]=f[i][j-i];a[i][j]=(node1){i,j-i};
36         go(k,1,i)
37             if(f[i][j]>f[k-1][j-(i-k+1)]+(k-1)*(g[i].dat-g[k-1].dat))
38                f[i][j]=f[k-1][j-(i-k+1)]+(k-1)*(g[i].dat-g[k-1].dat),a[i][j]=(node1){k-1,j-(i-k+1)};
39     }
40     int t1=n,t2=m;
41     while(t1)
42     {
43         if(t1==a[t1][t2].i){go(i,1,t1)ans[g[i].pos]++;}
44         else{go(i,a[t1][t2].i+1,t1)ans[g[i].pos]++;}
45         int tt=t1;t1=a[t1][t2].i;t2=a[tt][t2].j;
46     }
47     printf("%d\n",f[n][m]);
48     go(i,1,n)printf("%d ",ans[i]);
49     return 0;
50 }
View Code

 

转载于:https://www.cnblogs.com/forward777/p/11004240.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值