题目列表:P1122 P2016 P1352 P2015 P2014 P1273 P1272 P2458 P1131 P1270 P3177
P1122最大子树和
#include<bits/stdc++.h>
using namespace std;
int n;
int v[16005];
vector<int> vec[16005];
int mx=~0x3f3f3f3f,root=0;
int dp[16005];
void dfs(int cur,int fa){
dp[cur]=v[cur];
for(int i=0;i<vec[cur].size();i++){
int to=vec[cur][i];
if(to==fa)continue;
dfs(to,cur);
if(dp[to]<0)dp[to]=0;
dp[cur]+=dp[to];
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&v[i]);
if(mx<v[i]){
root=i;mx=v[i];
}
}
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
vec[a].push_back(b);
vec[b].push_back(a);
}
dfs(root,0);
cout<<dp[root]<<endl;
return 0;
}
P2016战略游戏
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> vec[1505];
int dp[1505][3];
void dfs(int cur,int fa){
dp[cur][0]=1;
for(int i=0;i<vec[cur].size();i++){
int to=vec[cur][i];
if(to==fa)continue;
dfs(to,cur);
dp[cur][0]+=min(dp[to][0],dp[to][1]);//放置士兵的位置
dp[cur][1]+=dp[to][0];//没放置士兵的位置
}
}
int book[1505];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int a;
scanf("%d",&a);
int t;
scanf("%d",&t);
for(int j=1;j<=t;j++){
int temp;
scanf("%d",&temp);
vec[a].push_back(temp);
book[temp]=1;
}
}
int root=find(book,book+n,0)-book;
dfs(root,-1);
int ans=min(dp[root][0],dp[root][1]);
cout<<ans<<endl;
return 0;
}
P1352没有上司的舞会 战略游戏升级版
#include<bits/stdc++.h>
using namespace std;
int n;
int v[6005];
vector<int> vec[6005];
int dp[6005][2];
int flag[6005];
void dfs(int cur,int fa){
dp[cur][1]=v[cur];
for(int i=0;i<vec[cur].size();i++){
int to=vec[cur][i];
if(to==fa)continue;
dfs(to,cur);
dp[cur][0]=max(dp[cur][0],dp[cur][0]+max(dp[to][1],dp[to][0]));//该位置没有上司
dp[cur][1]=max(dp[cur][1],dp[cur][1]+dp[to][0]);//该位置为上司
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&v[i]);
}
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
if(i==n)break;
vec[b].push_back(a);
flag[a]=1;
}
int ans=0;
for(int i=1;i<=n;i++){
if(flag[i])continue;
dfs(i,0);
ans+=max(dp[i][0],dp[i][1]);
}
cout<<ans;
return 0;
}
P2015二叉苹果树 树上分组背包
#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[105][105];
vector<int> vec[105];
int sz[105];
int dp[105][105];
void dfs(int cur,int fa){
sz[cur]=1;
for(int i=0;i<vec[cur].size();i++){
int to=vec[cur][i];
if(to==fa)continue;
dfs(to,cur);
sz[cur]+=sz[to];
for(int j=min(m,sz[cur]-1);j;j--){
for(int k=0;k<=min(j-1,sz[to]-1);k++){
dp[cur][j]=max(dp[cur][j],dp[to][k]+dp[cur][j-k-1]+v[cur][to]);
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
vec[a].push_back(b);
vec[b].push_back(a);
v[a][b]=c,v[b][a]=c;
}
dfs(1,0);
cout<<dp[1][m];
return 0;
}
P2014选课 同二叉苹果树 都是分组背包
#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[305];
vector<int> vec[305];
int sz[305];
int dp[305][305];
void dfs(int cur,int fa){
sz[cur]=1;
for(int i=0;i<vec[cur].size();i++){
int to=vec[cur][i];
if(to==fa)continue;
dfs(to,cur);
sz[cur]+=sz[to];
for(int j=min(m,sz[cur]-1);j;j--){
for(int k=0;k<=min(j-1,sz[to]-1);k++){
dp[cur][j]=max(dp[cur][j],dp[to][k]+dp[cur][j-k-1]+v[to]);
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
vec[i].push_back(a);
vec[a].push_back(i);
v[i]=b;
}
dfs(0,0);
cout<<dp[0][m];
return 0;
}
P1273有线电视网 树上分组背包
#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[3005];
int mp[3005][3005];
int money[3005];
vector<int> vec[3005];
int dp[3005][3005];
int sz[3005];
void dfs(int cur,int fa){
if(vec[cur].size()==0){
sz[cur]=1;
dp[cur][1]=money[cur];
return ;
}
for(int i=0;i<vec[cur].size();i++){
int to=vec[cur][i];
if(to==fa)continue;
dfs(to,cur);
sz[cur]+=sz[to];
for(int j=sz[cur];j;j--){
for(int k=0;k<=sz[to];k++){
if(j<k)break;
dp[cur][j]=max(dp[cur][j],dp[to][k]+dp[cur][j-k]-mp[cur][to]);
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n-m;i++){
int t;
scanf("%d",&t);
for(int j=1;j<=t;j++){
int a,b;
scanf("%d%d",&a,&b);
vec[i].push_back(a);
mp[i][a]=b;
mp[a][i]=b;
}
}
for(int i=1;i<=m;i++){
scanf("%d",&money[i+n-m]);
}
memset(dp,~0x3f,sizeof(dp));
for(int i=1;i<=n;i++){
dp[i][0]=0;
}
dfs(1,0);
for(int i=m;i;i--){
if(dp[1][i]>=0){
cout<<i<<endl;
return 0;
}
}
return 0;
}
P1272重建道路
#include<bits/stdc++.h>
using namespace std;
int n,p;
vector<int> vec[155];
int dp[155][155];
int sz[155];
void dfs(int cur,int fa){
sz[cur]=1;
dp[cur][1]=vec[cur].size();
for(int i=0;i<vec[cur].size();i++){
int to=vec[cur][i];
if(to==fa)continue;
dfs(to,cur);
sz[cur]+=sz[to];
for(int j=sz[cur];j>=1;j--){
for(int k=1;k<=sz[to];k++){
if(j<=k)continue;
dp[cur][j]=min(dp[cur][j],dp[cur][j-k]+dp[to][k]-1);
}
}
}
}
int book[155];
int main(){
scanf("%d%d",&n,&p);
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
vec[a].push_back(b);
book[b]=1;
}
int root=find(book+1,book+1+n,0)-book;
memset(dp,0x3f,sizeof(dp));
dfs(root,0);
int ans=0x3f3f3f3f;
for(int i=1;i<=n;i++){
ans=min(ans,dp[i][p]+((i==root)?0:1));
}
cout<<ans<<endl;
return 0;
}
P2458[SDOI2006]保安站岗 分类讨论 加维表示3种状态
因为不能忽略子节点对父节点的影响,所以需要三种状态
分别为1.在当前节点放置一个保安 2.在至少一个子节点放置保安 3.在父节点放置一个保安
至少存在一次
,否则补上
来满足至少一次
#include<bits/stdc++.h>
using namespace std;
int n;
int v[1505];
vector<int> vec[1505];
int dp[1505][3];
void dfs(int cur,int fa){
dp[cur][0]=v[cur];
int flag=0,sig=0x3f3f3f3f;
for(int i=0;i<vec[cur].size();i++){
int to=vec[cur][i];
if(to==fa)continue;
dfs(to,fa);
dp[cur][0]+=min(dp[to][0],min(dp[to][1],dp[to][2]));
if(dp[to][0]<=dp[to][1]){
flag=1;
dp[cur][1]+=dp[to][0];
dp[cur][2]+=dp[to][0];
}
else {
sig=min(sig,dp[to][0]-dp[to][1]);
dp[cur][1]+=dp[to][1];
dp[cur][2]+=dp[to][1];
}
}
dp[cur][1]+=(flag==0)?sig:0;
}
int book[1505];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
v[a]=b;
int t;
scanf("%d",&t);
for(int j=1;j<=t;j++){
int temp;
scanf("%d",&temp);
vec[a].push_back(temp);
book[temp]=1;
}
}
int root=find(book+1,book+1+n,0)-book;
dfs(root,0);
int ans;
ans=min(dp[root][0],dp[root][1]);
cout<<ans;
return 0;
}
P1131[ZJOI2007]时态同步
#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005
#define ll long long
ll n;
ll u[MAXN],v[MAXN],w[MAXN];
ll first[MAXN],nxt[MAXN],cnt;
void add(ll a,ll b,ll c){
++cnt;
nxt[cnt]=first[a];first[a]=cnt;
u[cnt]=a,v[cnt]=b,w[cnt]=c;
}
ll dp[MAXN];
ll ans=0;
void dfs(int cur,int fa){
ll mx=0;
for(int i=first[cur];i;i=nxt[i]){
int to=v[i];
if(to==fa)continue;
dfs(to,cur);
mx=max(mx,w[i]+dp[to]);
}
dp[cur]+=mx;
for(int i=first[cur];i;i=nxt[i]){
int to=v[i];
if(to==fa)continue;
ans+=mx-(w[i]+dp[to]);
}
}
int main(){
scanf("%lld",&n);
ll root;
scanf("%lld",&root);
for(ll i=1;i<n;i++){
ll a,b,c;
scanf("%lld%lld%lld",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
dfs(root,0);
cout<<ans<<endl;
return 0;
}
P1270“访问”美术馆
#include<bits/stdc++.h>
using namespace std;
#define MAXN 405
int u[MAXN],v[MAXN],w[MAXN];
int cnt,first[MAXN],nxt[MAXN];
void add(int a,int b,int c){
++cnt;
nxt[cnt]=first[a];first[a]=cnt;
u[cnt]=a;v[cnt]=b;w[cnt]=c;
}
int dp[MAXN][2005];
int flag[MAXN];
pair<int,int> mp[MAXN];
int n;
bool vis[MAXN];
void pre(int cur,int fa){
flag[fa]++;
if(flag[fa]>2){
return ;
}
vis[cur]=1;
add(cur,fa,mp[cur].first*2);
add(fa,cur,mp[cur].first*2);
for(int i=0;i<=mp[cur].second;i++){
dp[cur][i]=5*i;
}
if(mp[cur].second!=0)return ;
for(int i=1;i<=n;i++){
if(vis[i])continue;
pre(i,cur);
}
}
int sz[MAXN];
void dfs(int cur,int fa){
sz[cur]=mp[cur].second;
for(int i=first[cur];i;i=nxt[i]){
int to=v[i];
if(to==fa)continue;
dfs(to,cur);
sz[cur]+=sz[to];
for(int j=sz[cur];j>=0;j--){
for(int k=1;k<=sz[to];k++){
if(j<k)break;
dp[cur][j]=min(dp[cur][j],dp[to][k]+dp[cur][j-k]+w[i]);
}
}
}
}
int main(){
/*
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
*/
int tme;
scanf("%d",&tme);
int a,b,m=0;
while(~scanf("%d%d",&a,&b)){
++n;
mp[n]=pair<int,int>(a,b);
m+=b;
}
memset(dp,0x3f,sizeof(dp));
pre(1,0);
dp[0][0]=0;
dfs(0,-1);
for(int i=m;i>=0;i--){
if(dp[0][i]<tme){
cout<<i<<endl;
return 0;
}
}
return 0;
}
P3177[HAOI2015]树上染色
权值val不只是在当前根节点的子树内,而是对于整棵树来说的,相当于将一颗树以这条边分成两半
并且这个权值是动态的,状态不同权值也不相同,所以需要在dfs的过程中计算
#include<bits/stdc++.h>
using namespace std;
#define MAXN 4005
#define ll long long
int n,m;
int u[MAXN],v[MAXN],w[MAXN];
int cnt,first[MAXN],nxt[MAXN];
void add(int a,int b,int c){
++cnt;
nxt[cnt]=first[a];first[a]=cnt;
u[cnt]=a,v[cnt]=b,w[cnt]=c;
}
int sz[MAXN];
ll dp[MAXN][MAXN];
void dfs(int cur,int fa){
dp[cur][0]=dp[cur][1]=0;
sz[cur]=1;
for(int i=first[cur];i;i=nxt[i]){
int to=v[i];
if(to==fa)continue;
dfs(to,cur);
sz[cur]+=sz[to];
for(int j=min(m,sz[cur]);j>=0;j--){
for(int k=0;k<=sz[to];k++){
if(j<k)break;
if(dp[cur][j-k]==-1)continue;//此边的另外一部分 状态的最多黑点数比较少 可能无法达到j-k个 属于不合法状态 所以当前状态需要直接跳过
int numl=k,numr=m-k;
ll val=1ll*((n-numr-sz[to])*(sz[to]-numl)+numl*numr)*w[i];//因为是整棵树的价值,所以需要用n来减
dp[cur][j]=max(dp[cur][j],dp[to][k]+dp[cur][j-k]+val);
}
}
}
}
int book[MAXN];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);add(b,a,c);
book[b]=1;
}
int root=find(book+1,book+n+1,0)-book;
memset(dp,-1,sizeof(dp));
dfs(root,0);
cout<<dp[root][m]<<endl;
return 0;
}
D - Serval and Rooted Tree
每个叶节点的值不确定,但是可以改变思路,从大小顺序入手。
dp[i]表示第i个结点的值为第dp[i]大。
若为max结点,dp[i]等于所有子结点中最小的dp[j],越靠前越大。
若为min结点,dp[i]等于所有子结点中dp[j]的和,最大为第dp[j]大。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 300005
#define inf 0x3f3f3f3f
int flag[MAXN];
int sz[MAXN];
int dp[MAXN];
vector<int> pv[MAXN];
void dfs(int cur,int fa){
if(pv[cur].size()==0){
dp[cur]=1;
sz[cur]=1;
return ;
}
if(flag[cur])dp[cur]=inf;
for(int i=0;i<pv[cur].size();i++){
int to=pv[cur][i];
if(to==fa)continue;
dfs(to,cur);
sz[cur]+=sz[to];
if(flag[cur]){
dp[cur]=min(dp[to],dp[cur]);
}
else {
dp[cur]+=dp[to];
}
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&flag[i]);
}
for(int i=2;i<=n;i++){
int t;
scanf("%d",&t);
pv[t].push_back(i);
}
dfs(1,0);
cout<<sz[1]+1-dp[1]<<endl;
return 0;
}