ACM DP作业整理 待补

 

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值