https://vjudge.net/contest/341779#overview
难度中等
题解:
A:
二进制拆分 + 01背包;
n可以拆分为 1、2、4 …… 2 ^ (k - 1)、n - 2 ^ k + 1
m = 100000,复杂度是 mnlogn,不知道为啥能过……
B:
完全背包,滚动数组优化;
C:
01背包;
D:
一个包装的01背包问题,将钱数看作价值,被捕的概率看作体积,但是注意概率不能靠加减法计算,而是乘除法;
考虑到数组下标不能是小数,所以让下标变成价值,那么从大到小遍历所有可能的价值,第一个体积小于总体积的价值就是答案;
用滚动数组优化;
定义dp[i] : 表示 抢劫 i 价值的财物被捕的最小概率;
dp[0] = 0,dp[i] = INF;
转移:dp[j] = min(dp[j],dp[j - w[i]] * (1.0 - v[i]) + (1.0 - dp[j - w[i]]) * v[i] + dp[j - w[i]] * v[i]);
但是这样实在是太长了,考虑换一种状态;
dp[i] : 表示 抢劫 i 价值的财物逍遥法外的最大概率;
dp[0] = 1,dp[i] = 0;
转移 :dp[j] = max(dp[j],dp[j - w[i]] * (1.0 - v[i]));
注意不要漏掉状态(加粗部分),找出状态的所有情况是关键点
E:
证明你学过动态规划;
F:
在另一篇博文里讲的很清楚了;
https://blog.csdn.net/Loi_gold/article/details/103192107
G:
证明你学过动态规划;
H:
不难,但还是想了很长时间,首先要优化状态,让差的平方最小,则考虑到排序;
可以证明,排序后,当且仅当某数和前面一个数或者它后面一个数成对时,总的和最小;
因为 对于4个数,差是a,b,c。(a + b)^ 2 <= (a + c) ^ 2 + (b + c) ^ 2 和 (a + b + c) ^ 2 + b ^ 2;
dp[i][j] : 前i个数选2 * j个数字D的最小值;
dp[i][j] = min( dp[i - 1][j],dp[i - 2][j - 1] + (a[i] - a[i - 1]) * (a[i] - a[i - 1]) );
注意初始化dp[i][0] = 0; 因为dp[i][j] 会由此状态转移过去;
因为这个点,WA了很长时间。
所以当确定方程状态正确时,应注意边界和初始化是否正确;
I:
树形DP,和J题差不多;
J:
树形DP;
dp[ii][0/1] : 表示是否选i点;
dp[i][0] += max(dp[v][1],do[v][0]); v是i的子结点;
dp[i][1] += dp[v][0];
K:
树形DP + 背包:
dp[x][i][j],表示以x为根的子树中,前i个子结点,攻破j个城市获得最大的宝物价值;
用滚动数组可以优化掉一维;
代码:
A:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 100001 + 11;
int v[MAXN],dp[MAXN],cnt[MAXN];
int n,V,ans,tot,t;
void solve(){
memset(dp,0,sizeof(dp));
dp[0] = 1,ans = 0,tot = n;
for(int i = 1;i <= n;i ++) scanf("%d",&v[i]);
for(int i = 1;i <= n;i ++){
scanf("%d",&cnt[i]);
t = 1;
while(1){
if((t << 1) * 2 - 1 >= cnt[i]) break;
t <<= 1;
v[++ tot] = t * v[i];
}
v[++ tot] = (cnt[i] - (t << 1) + 1) * v[i];
}
for(int i = 1;i <= tot;i ++)
for(int j = V;j >= v[i];j --)
dp[j] |= dp[j - v[i]];
for(int i = 1;i <= V;i ++) if(dp[i]) ans ++;
cout << ans << "\n";
return;
}
int main(){
while(cin >> n >> V && n && V)
solve();
return 0;
}
D:
第一种:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 20001 + 22;
int n,T,sum;
int w[MAXN];
double V,v[MAXN],dp[MAXN],inf = 214483.0;
void solve(){
sum = 0;
cin >> V >> n;
for(int i = 1;i <= n;i ++){
cin >> w[i] >> v[i];
sum += w[i];
}
for(int i = 0;i <= sum;i ++) dp[i] = inf;
dp[0] = 0.0;
for(int i = 1;i <= n;i ++)
for(int j = sum;j >= w[i];j --)
if(dp[j - w[i]] <= 1.0){
double t = dp[j - w[i]] * (1.0 - v[i]) + (1.0 - dp[j - w[i]]) * v[i] + dp[j - w[i]] * v[i];
dp[j] = min(dp[j],t);
}
for(int i = sum;i >= 0;i --){
if(dp[i] <= V){
cout << i << endl;
return;
}
}
}
int main(){
cin >> T;
while(T --)
solve();
return 0;
}
第二种:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 20001 + 22;
int n,T,sum;
int w[MAXN];
double V,v[MAXN],dp[MAXN],inf = 214483.0;
void solve(){
sum = 0;
cin >> V >> n;
for(int i = 1;i <= n;i ++){
cin >> w[i] >> v[i];
sum += w[i];
}
for(int i = 0;i <= sum;i ++) dp[i] = inf;
dp[0] = 0.0;
for(int i = 1;i <= n;i ++)
for(int j = sum;j >= w[i];j --)
if(dp[j - w[i]] <= 1.0){
double t = dp[j - w[i]] * (1.0 - v[i]) + (1.0 - dp[j - w[i]]) * v[i] + dp[j - w[i]] * v[i];
dp[j] = min(dp[j],t);
}
for(int i = sum;i >= 0;i --){
if(dp[i] <= V){
cout << i << endl;
return;
}
}
}
int main(){
cin >> T;
while(T --)
solve();
return 0;
}
H:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 2001 + 22;
int a[MAXN],ma[MAXN],dp[MAXN][MAXN];
int n,k;
void solve(){
memset(dp,0x3f3f,sizeof(dp));
dp[0][0] = 0;
for(int i = 1;i <= n;i ++)
scanf("%d",&a[i]),dp[i][0] = 0;
sort(a + 1,a + n + 1);
for(int i = 1;i <= n;i ++)
ma[i] = (a[i] - a[i - 1]) * (a[i] - a[i - 1]);
for(int i = 2;i <= n;i ++)
for(int j = 1;j * 2 <= i;j ++)
dp[i][j] = min(dp[i - 2][j - 1] + ma[i] , dp[i - 1][j]);
if(k) cout << dp[n][k] << endl;
else cout << "0" << endl;
}
int main(){
while(cin >> n >> k)
solve();
return 0;
}
I:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
vector<int>G[1603];
int dp[1603][2];
int n;
void dfs(int x,int f){
dp[x][0] = 0,dp[x][1] = 1;
for(int i = 0;i < G[x].size();i ++){
int v = G[x][i];
if(v == f) continue;
dfs(v,x);
dp[x][0] += dp[v][1];
dp[x][1] += min(dp[v][0],dp[v][1]);
}
return;
}
void solve(){
for(int i = 0;i < n;i ++) G[i].clear();
for(int i = 1;i <= n;i ++){
int x,m,y;
scanf("%d:(%d)",&x,&m);
while(m --){
scanf("%d",&y);
G[x].push_back(y);
G[y].push_back(x);
}
}
dfs(0,-1);
cout << min(dp[0][0],dp[0][1]) << endl;
return;
}
int main(){
while(~scanf("%d",&n))
solve();
return 0;
}
J:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 10001;
int fa[MAXN],a[MAXN],dp[MAXN][2];
int n,root;
vector<int>G[MAXN];
void dfs(int x){
for(int i = 0;i < G[x].size();i ++){
int v = G[x][i];
dfs(v);
dp[x][0] += max(dp[v][0],dp[v][1]);
dp[x][1] += dp[v][0];
}
return;
}
void solve(){
while(~scanf("%d",&n)){
for(int i = 1;i <= n;i ++){
scanf("%d",&a[i]);
dp[i][1] = a[i];
dp[i][0] = 0;
G[i].clear();
fa[i] = -1;
}
while(1){
int x,v;
scanf("%d%d",&x,&v);
if(!x && !v) break;
fa[x] = v;
G[v].push_back(x);
}
int x = 1;
while(fa[x] != -1) x = fa[x];
root = x,dfs(root);
cout << max(dp[root][0],dp[root][1]) << endl;
}
}
int main(){
solve();
return 0;
}
K:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN = 205;
int size[MAXN],a[MAXN],dp[MAXN][MAXN];
int n,m,ans;
vector<int>G[MAXN];
void init(){
memset(dp,0,sizeof(dp));
for(int i = 0;i <= n;i ++) size[i] = 1,G[i].clear();
return;
}
void Dfs(int x){
dp[x][1] = a[x];
for(int i = 1;i <= G[x].size();i ++){
Dfs(G[x][i - 1]);
for(int j = m + 1;j >= 1;j --)
for(int k = 1;k < j;k ++)
dp[x][j] = max(dp[x][j],dp[x][j - k] + dp[G[x][i - 1]][k]);
}
return;
}
void solve(){
int f,c;
init();
for(int i = 1;i <= n;i ++){
scanf("%d%d",&f,&a[i]);
G[f].push_back(i);
}
Dfs(0);
cout << dp[0][m + 1] << endl;
return;
}
int main(){
while(cin >> n >> m){
if(!n && !m) break;
solve();
}
return 0;
}