紫书DP章节部分例题

例题 9-10 UVA 1626

VOJ题目链接
题意:给出由小括号与中括号构成的序列,序列可以为空。要求输出修补后最短的全部匹配的括号序列。
思路:DP[i , j] 为 下标为[i, j)的最短长度
1.当 arr[i] == '(' ;arr[1] == ')'时 DP[i, j] = DP[i+1, j-1];
2.DP[i, j] = min(DP[i, j], DP[i, k] + DP[k, j])。
3.在DP时记录下转换为当前最优状态的位置,打印结果时递归打印。
注意:情况二任何情况下都必须处理,如遇到()(),不处理情况二为错误运算。题目输入包含空字符串,并且最后一行有空行但是不能有多余的空行。

#include<bits/stdc++.h>
using namespace std;
typedef  long long LL ;
typedef unsigned long long ULL;
#define mp make_pair
typedef pair<int, int> P;
const int maxn = 111;
const double eps = 1e-6;
const int  MOD = 20071027;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
int T;

struct node
{
	P a;
	P b;
	node(P x = mp(0, 0), P y= mp(0, 0))
	{
		a = x;  b  = y;
	}

};
	string arr;
int DP[maxn][maxn];
node ans[maxn][maxn];

void print(int L, int R)
{
	if(L>=R)
		return;
	if(L==R-1)
	{
		if(arr[L]=='(' || arr[L] == ')')
			printf("()");
		if(arr[L]=='[' || arr[L] == ']')
			printf("[]");
		return;
	}
	P u  = ans[L][R].a;
	P v  = ans[L][R].b;
	if(u==v)//当前最左端最右端构成括号匹配直接递归输出
	{
		printf("%c", arr[L]);
		print(L+1, R-1);
		printf("%c", arr[R-1]);
	}
	else
	{
		print(u.first, u.second);
		print(v.first, v.second);
	}
	return;
}
void solve()
{
	memset(ans, 0, sizeof ans);
	for(int i=0;i<maxn;++i)
		for(int k=0;k<maxn;++k)
			DP[i][k]  = INF;
	getline(cin , arr);
	getline(cin , arr);
	int len  = arr.size();
	for(int i=0;i<len;++i)
	{
		DP[i][i] = 0;
		DP[i][i+1] = 1;
		ans[i][i]= node{mp(i, i), mp(i, i)};
		ans[i][i+1]  = node{mp(i, i+1), mp(i+1, i+1)};
	}


	for(int i=2;i<=len;++i)
	{
		for(int L = 0;L+i<=len;++L)
		{
			int R  =L  + i;
			if((arr[L]=='(' && arr[R-1] == ')') || (arr[L] =='[' && arr[R-1] == ']'))
			{
				DP[L][R]  = DP[L+1][R-1];
				ans[L][R] =node(mp(L+1, R-1), mp(L+1, R-1));
			}
			for(int j =  L+1;j<R;++j)
			{
				//printf("DP[%d][%d] = %d , DP[%d][%d]  = %d, DP[%d][%d]  = %d\n", L, R  , DP[L][R], L, j, DP[L][j],  j, R, DP[j][R]);
				if(DP[L][R] > DP[L][j] + DP[j][R])
				{
					DP[L][R] = DP[L][j] + DP[j][R];
					ans[L][R] =node{mp(L, j), mp(j, R)};
				}
			}
			//printf("DP[%d][%d] = %d\n", L, R, DP[L][R]);
		}
	}

   // printf("%s\n", arr.c_str());
	print(0, len);
	printf("\n");
	if(T) printf("\n");
}
int main()
{
	cin >> T;
	getchar();
	while(T--)
	solve();
	return 0;
}

一直不懂ios::sync_with_stdio(false)加速读入的原理,这题 getline(),和ios加速混用就会报错。这个地方WA了两个小时...

例题9-12 UVA 12186

VOJ题目链接

题意:给出一颗有根树,叶子节点标记后可向父节点加1点权,当一个节点的所子节点加权百分比达到T后,该节点同样向父节点加权。问要使根节点向上加权,最少要标记多少叶子节点。

思路:树上DP, DP[u]表示要使u向上加权的标记叶子节点数。算出节点u在T的百分比下至少需要c个子节点提交。算出所有子节点DP值,排序后最小的c个之和即为DP[u]。

代码

#include<bits/stdc++.h>
using namespace std;
typedef  long long LL ;
typedef unsigned long long ULL;
#define mp make_pair
typedef pair<int, int> P;
const int maxn = 1e5+5;
const double eps = 1e-6;
const int  MOD = 20071027;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
int N, T;

vector<int> vex[maxn];


int dp(int root)
{
	vector<int> rec;
	int son = vex[root].size();
	if(!son)
	{
		return 1;
	}
	int num =  (son * T-1)/100+1;
	for(int i=0;i<son;++i)
		rec.push_back(dp(vex[root][i]));
	sort(rec.begin(), rec.end());
	int ans =  0;
	for(int i=0;i<num;++i)
	{
		ans += rec[i];
		//printf("%d\n", rec[i]);
		
	}
	return  ans;
}

void solve()
{
	for(int i=0;i<=N;++i)
		vex[i].clear();
	for(int i=1;i<=N;++i)
	{
		int tem;cin >> tem;
		vex[tem].push_back(i);
	}

	int ans  = dp(0);
	printf("%d\n", ans);

}


int main()
{
    ios::sync_with_stdio(false);
	while(cin >> N >>T)
	{
		if(!(N || T))
			return 0;
		solve();
	}

}

例题9-13 UVA 1220

VOJ题目链接

题意:求树的最大独立集,并判断最大值的取法是否唯一。

思路:由于要判断是否唯一,因此在节点是否选取时需要额外的标记唯一信息,不能直接按照前两页的方法直接DP,因此

DP[u] [0] 表示u节点未选取时的最大独立集,DP[u] [1] 表示 节点选取时的最大独立集。有

DP[u] [1] = \(\sum\)DP[v] [0] + 1 ;F[u] [1] = \(\bigcap\) F[v] [0] :当节点选取时所有子节皆不能选取,且所有子节点唯一时,该节点唯一。

DP[u] [0] = \(\sum\) max(DP[v] [1], DP[v] [0]]), 若要使该节点唯一则, 所有子节点的选取与不选取两个状态独立集数量不同,且所选取的状态也要唯一。

#include<bits/stdc++.h>
using namespace std;
typedef  long long LL ;
typedef unsigned long long ULL;
#define mp make_pair
typedef pair<int, int> P;
const int maxn = 1e5+5;
const double eps = 1e-6;
const int  MOD = 20071027;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
int T;


map<string, int> name;
vector<int> vex[205];
int uni[205][2] ;
int dp[205][2];

void edge (string x)
{
	int m  =name.size();
	if(name.count(x)==0)
		name[x]=  m;
	return;
}

void dfs(int root, int fa)
{
	for(int i=0;i<vex[root].size();++i)
	{
		set<int> rec;
		int m  =vex[root][i];
		if(m!=fa)
		{
		    dfs(m, root);
			dp[root][1] += dp[m][0];
			uni[root][1] &= uni[m][0];
			if(dp[m][0]>dp[m][1])
			{
				dp[root][0] += dp[m][0];
				uni[root][0] &= uni[m][0];
			}
			else if(dp[m][0]<dp[m][1])
			{
				dp[root][0] += dp[m][1];
				uni[root][0] &= uni[m][1];

			}
			else if(dp[m][0]==dp[m][1])
			{
				uni[root][0] = 0;
				dp[root][0] += dp[m][0];
			}
		}

	}
	dp[root][1] += 1;
		return;
}

void solve()
{
	memset(dp, 0, sizeof dp);
	for(int i=0;i<=203;++i)
	{
		uni[i][0] = 1;
		uni[i][1] = 1;
	}
	for(int i=0;i<203;++i)
		vex[i].clear();
	name.clear();
	string arr;
	string arr2;
	cin >> arr;
	edge(arr);
	for(int i=0;i<T-1;++i)
	{
		cin >> arr >> arr2;
		edge(arr);edge(arr2);
		int u = name[arr]  ;
		int v=  name[arr2];
		vex[u].push_back(v);
		vex[v].push_back(u);
	}
//	for(auto c:name)
//	{
//		cout << c.first << " " << c.second << endl;
//	}

	dfs(0, -1);
	int ans  =0;
	int uni_ans = 0;
	if(dp[0][0]==dp[0][1])
	{
		ans =dp[0][0];
		uni_ans =  0;
	}
	else if(dp[0][1]>dp[0][0])
	{
		ans =dp[0][1];
		uni_ans =  uni[0][1];
	}
	else if(dp[0][1] < dp[0][0])
	{
		ans =dp[0][0];
		uni_ans =  uni[0][0];
	}
	string tem;
	if(uni_ans==1)
		tem = "Yes";
	else
		tem = "No";

	printf("%d %s\n", ans, uni_ans==1?"Yes":"No");
	return;

}

int main()
{
    ios::sync_with_stdio(false);
	while(cin >> T && T)
		solve();

}

例题9-14 UVA 1218 Perfect Service

VOJ题目链接

题意:给出10000个节点的无环图,可以选取节点为服务器。最后要求不是服务器的节点仅能与一个服务器节点相邻,但对标记节点无要求。问最少的标记节点数是多少。

思路:任选一个节点为根,DP[u] [0] 表示节点u是服务器,DP[u] [1] 表示节点u不是服务器但父节点是服务器。 DP[u] [2] 表示当前节点与父节点都不是服务器。

v表示u的子节点。

DP[u] [0] =( \(\sum\)min(DP[v] [0] , DP[v] [1]) )+ 1

当前节点是服务器 ,则子节点可以是服务器,也可以不是服务器。

DP[u] [1] = (\(\sum\) DP[V] [2])

当前节点不是服务器,但父节点是服务器。则子节点必不能服务器,否则一个节点与两个服务器相邻,不符合题意。

DP [u] [2] = min(DP[u] [1] - DP[v] [2] + DP[v] [0] )

当前节点及其父节点都不是服务器,则子节点中只能有一个服务器节点。可以利用DP[u] [1] 表示所有不是服务器的子节点之和,可以使用来加快运算。

在DP过程中间将叶子节点的DP[ u] [ 2]标识为INF。叶子节点及其父节点均不为服务器,当前状态不符合题意不可用。

#include<bits/stdc++.h>
using namespace std;
#define  mp make_pair
#define REP(i,a,b) for(int i=a;i<b;i++)
typedef  long long LL ;
typedef unsigned long long ULL;
typedef pair<int, int> P;
const int maxn = 1e4+10;
const LL MOD = 998244353;
const int INF = 1e4+7;
const double PI = acos(-1);
const double eps = 1e-7;

vector<int> vex[maxn];
int N;
int DP[maxn][3];
int judge;

void dfs(int fa, int now)
{
	if(vex[now].size()==1 && fa!=-1)
	{
		DP[now][0]= 1;
		DP[now][1]  = 0;
		DP[now][2]  = maxn;
		return;
	}
	for(int i=0;i<vex[now].size();++i)
	{
		if(vex[now][i]!=fa)
		{
			dfs(now, vex[now][i]);
			DP[now][0] += min(DP[vex[now][i]][1] , DP[vex[now][i]][0] );
			DP[now][1] += DP[vex[now][i]][2];
		}
	}
	DP[now][2] = INF;
	DP[now][0]+=1;
	for(int i=0;i<vex[now].size();++i)
		if(fa!= vex[now][i])
		DP[now][2]=  min(DP[now][2] , DP[now][1]-DP[vex[now][i]][2] + DP[vex[now][i]][0]);
	return;
}

void solve()
{
	for(int i=1;i<=N;++i)
	{
		vex[i].clear();
		DP[i][0]=DP[i][1]=DP[i][2] = 0;

	}
	REP(i, 0, N-1)
	{
		int a, b;cin >> a >> b;
		vex[a].push_back(b);
		vex[b].push_back(a);
	}
	cin >> judge;
	dfs(-1, 1);
	printf("%d\n", min(DP[1][0], DP[1][2]));
}

int main()
{
	while(cin >> N && judge !=-1)
	{
		solve();
		if(judge==-1) return  0;	
	}
	return 0;
}

posted on 2021-05-13 13:55  安乐最值钱  阅读(60)  评论(0)    收藏  举报

导航