NC16671 [NOIP2006]金明的预算方案
题目链接
关键点:
1、动态转移选主件元件,对于dp[j],表示选则主件,和若干附加,不超过j元的最大价值
2、动态转移有四种情况:
(1)不选主件
(2)选主件,不选附件
(3)选主件,选一个附件(在花费允许的情况下)
(4)选主件,选两个附件(一个主件最多两个附件)(在花费允许的情况下)
dp[j] = max(dp[j], dp[j-mainw[i]]+mainv[i]);
if (j-mainw[i]>=secw[i][1])
dp[j] = max(dp[j], dp[j-mainw[i]-secw[i][1]]+mainv[i]+secv[i][1]);
if (j-mainw[i]>=secw[i][2])
dp[j] = max(dp[j], dp[j-mainw[i]-secw[i][2]]+mainv[i]+secv[i][2]);
if (j-mainw[i]>=secw[i][2]+secw[i][1])
dp[j] = max(dp[j], dp[j-mainw[i]-secw[i][2]-secw[i][1]]+mainv[i]+secv[i][2]+secv[i][1]);
用一个数组专门存主件的价值和花费,另一个二维数组存附件(对应主件)的价值花费
3、因为是一维的动态转移,因此我们将花费遍历倒过来
完整代码
# include <bits/stdc++.h>
using namespace std;
int n, m;
int mainw[70], mainv[70];
int secw[70][5], secv[70][5];
int dp[32000+10];
int main()
{
cin>>n>>m;
for (int i=1; i<=m; i++)
{
int v, p, q;
cin>>v>>p>>q;
if (!q)
{
mainw[i] = v;
mainv[i] = p*v;
}
else
{
secw[q][0]++;
secw[q][secw[q][0]] = v;
secv[q][secw[q][0]] = v*p;
}
}
for (int i=1; i<=m; i++)
{
for (int j=n; mainw[i]&&j>=mainw[i]; j--)
{
dp[j] = max(dp[j], dp[j-mainw[i]]+mainv[i]);
if (j-mainw[i]>=secw[i][1])
dp[j] = max(dp[j], dp[j-mainw[i]-secw[i][1]]+mainv[i]+secv[i][1]);
if (j-mainw[i]>=secw[i][2])
dp[j] = max(dp[j], dp[j-mainw[i]-secw[i][2]]+mainv[i]+secv[i][2]);
if (j-mainw[i]>=secw[i][2]+secw[i][1])
dp[j] = max(dp[j], dp[j-mainw[i]-secw[i][2]-secw[i][1]]+mainv[i]+secv[i][2]+secv[i][1]);
}
}
cout<<dp[n]<<endl;
return 0;
}
NC14699 队伍配置
题目链接
关键点:
1、对于从者的选择,我们只考虑选择5个,对于礼装,我们考虑选择不超过从者的个数
2、选择前i个从者和前j个礼装,我们可以利用滚动数组减去一维
3、设dp[i][j][k],表示在cost=i的前提下,选择j个从者,k个礼装的最大价值
4、我们可以先处理礼装(k)=0,情况下,最多选择5个从者的最大价值,注意cost要倒过来遍历(少了一维)
5、然后在输入礼装数据时,在遍历礼装的个数
完整代码:
# include <bits/stdc++.h>
using namespace std;
int dp[140][10][310];//代表i的cost下,选j个从者,装备k个礼装的最大价值
int ans, n, m, d;
int main()
{
cin>>n>>m>>d;
for (int i=1; i<=n; i++)
{
int x, y;
cin>>x>>y;
for (int i=d; i>=y; i--)
{
for (int j=1; j<=5; j++)
{
dp[i][j][0] = max(dp[i][j][0], dp[i-y][j-1][0]+x);
ans = max(dp[i][j][0], ans);
}
}
}
for (int i=1; i<=m; i++)
{
int x, y;
cin>>x>>y;
for (int i=d; i>=y; i--)
{
for (int j=1; j<=5; j++)
{
for (int k=1; k<=j; k++)
{
dp[i][j][k] = max(dp[i][j][k], dp[i-y][j][k-1]+x);
ans = max(dp[i][j][k], ans);
}
}
}
}
cout<<ans<<endl;
return 0;
}
NC235951 草药大师
题目链接
关键点:
1、因为最大的时间上限很大,直接遍历会超时,这里利用map来遍历,mp[i] = j,表示时间为i的情况下,最多采集的价值为j。可以发现在背包中,每次被更新的时间只会在所有草药(小于限定的s)中排列加减得到。
2、一维的map来代表dp数组,要将map倒着遍历
3、最后在所有的时间中取最大值即可
4、记得要初始化dp[0] = 0;
完整代码:
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, s;
map<ll, ll>mp;
ll t[110], v[110];
int main()
{
cin>>n>>s;
for (int i=1; i<=n; i++)
{
cin>>t[i]>>v[i];
}
mp[0] = 0;
for(int i=1;i<=n;i++){
for(auto it=rbegin(mp);it!=rend(mp);it++){
if(it->first+t[i]<=s)
mp[it->first+t[i]]=max(mp[it->first+t[i]],mp[it->first]+v[i]);
}
}
ll ans=0;
for (auto it=mp.begin(); it!=mp.end(); it++)
ans = max(it->second, ans);
cout<<ans<<endl;
return 0;
}