紫书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
题意:给出一颗有根树,叶子节点标记后可向父节点加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
题意:求树的最大独立集,并判断最大值的取法是否唯一。
思路:由于要判断是否唯一,因此在节点是否选取时需要额外的标记唯一信息,不能直接按照前两页的方法直接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
题意:给出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;
}
浙公网安备 33010602011771号