《算法进阶指南》线性DP 饼干
2 题解
与排队打水类似,首先可以知道要让饼干数随着g[i]递减而递减。状态定义为前i个人共j个饼干的最小怒气值f[i][j]。
需要注意到当第i人饼干>1时,前i人共同减少一个饼干结果不变。当第i人饼干数=1时,遍历从第k个人开始饼干数都为1.
这样就能覆盖所有饼干分配方案。每一对[i][j]在这两种情况中曲最小值即可。
细节上,需要定义f[0][0]=0其他为INF,这是因为f[0][0]为n最小的合法情况(f[0][1],f[0][2]不合法)并且其他情况都可由f[0][0]扩展得出。
最后需要输出具体方案,在每个状态得出最小值时建立反边,最后从{n,m}反向回归,考虑不同状态转移时对答案的影响不同
struct Node
{
int x;
int y;
};
int n, m;
int f[31][5003];
int g[31], preg[31] = {0};
vector<Node> dag[31][5003];
int ans[31];
pair<int, int> id[31];
void solve()
{
memset(f, 0x7f, sizeof(f));
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> g[i];
id[i].first = g[i];
id[i].second = i;
}
sort(id + 1, id + 1 + n, greater<pair<int, int>>());
sort(g + 1, g + 1 + n, greater<int>());
for (int i = 1; i <= n; i++)
{
preg[i] = preg[i - 1] + g[i];
}
f[0][0] = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (j < i)
{
continue;
}
int x, y;
for (int k = 1; k <= i; k++)
{
if (f[i][j] > f[k - 1][j - i + k - 1] + (preg[i] - preg[k - 1]) * (k - 1))
{
f[i][j] = f[k - 1][j - i + k - 1] + (preg[i] - preg[k - 1]) * (k - 1);
x = k - 1, y = j - i + k - 1;
}
}
int ok = 0;
if (f[i][j] > f[i][j - i])
{
ok = 1;
f[i][j] = min(f[i][j], f[i][j - i]);
dag[i][j].push_back({i, j - i});
}
if (ok == 0)
{
dag[i][j].push_back({x, y});
}
}
}
int nowx = n, nowy = m, add = 0;
while (dag[nowx][nowy].size())
{
for (int i = 0; i < dag[nowx][nowy].size(); i++)
{
int nextx = dag[nowx][nowy][i].x, nexty = dag[nowx][nowy][i].y;
if (nextx == nowx)
{
add++;
}
else
{
for (int j = nowx; j > nextx; j--)
{
ans[id[j].second] = add + 1;
}
}
nowx = nextx, nowy = nexty;
}
}
cout << f[n][m] << endl;
for (int i = 1; i <= n; i++)
{
cout << ans[i] << ' ';
}
}
signed main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false), cin.tie(0);
int T = 1;
// cin >> T;
for (int i = 1; i <= T; i++)
{
solve();
// Init();
}
return 0;
}
3 收获
1.dp如何定义状态的方法,a.从题目中寻找能够减少状态总数的性质(这题为按g[i]递增,去除了非递增的饼干分配方案)(可能无法减少状态总数)b.设计的转移能够涵盖所有状态
2.需要求具体方案的dp,两种方法。方法a.在每个状态要转移时记录下唯一的转移路线(在能合法达到当前状态的所有状态取最值)最后从最终状态回推(状态本身不一定是答案,注意不同状态的转移对答案的影响)
方法b.从最终状态开始,遍历能合法抵达当前状态的所有状态(基本与正推相同),如果上一个状态经转移方程后等于当前状态,说明当前状态就是由这个状态转移得出,这样不断向前。
3.定义边界,保证不漏不多,不能让非法状态得到转移,并且从边界出发能涵盖所有需要的状态。

浙公网安备 33010602011771号