hdu3586 and 5834(树形dp)
🎈Information Disturbing
题意
在战场上,击败敌人的有效方法是破坏他们的通讯系统。
信息部门告诉您,敌军有n名,其网络具有n-1条通信路线,可以覆盖所有士兵。信息可以通过通信路径在任何两个士兵之间交换。第一名士兵是总司令,只有一个邻居的其他士兵是前线士兵。
您的老板zzn命令您切断一些路线,以使网络中的任何前线士兵都无法将他们从战场上收集到的信息反映给总指挥官(第一名士兵)。
有一种设备可以选择一些路线来切断。但是,您选择切断的任何一条路径的成本(w)都不会超过设备的上限功率。费用总和不能超过设备的使用寿命m。
现在,请最小化设备的上限功率以完成任务
思路
首先,要切割的边总和要小于M,求割掉的边权最大值最小化,二分答案跑树,复杂度n * logm
其次,二分判断dfs怎么写,肯定是从1递归下去到子叶节点,把除了一条边的点赋值为m+1,其他赋值为0,自下往上判断
如果值大于等于边权,说明这条能剪掉,但要判断是否这条减掉之后利益化最大,所以转移方程是dp[u]+=min(dp[v],w);
如果值小于边权,说明这条不能减掉,dp[u]+=dp[v];
然后判断dp[1]是否小于等于m(这里就是为什么把一条边的点赋值为m+1的原因,万一没剪干净,就直接out)
AC代码
#include <bits/stdc++.h>
#define endl '\n'
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS ios::sync_with_stdio(false)
#define ll long long
#define mod 1000000007
#define inf 0x3f3f3f3f
using namespace std;
const int N=1e3+10;
int n,m;
int head[N],to[N<<1],ne[N<<1],cost[N<<1];
int tot;
int vis[N];
void init(){
mem(head,-1);
tot=0;
}
void add(int u,int v,int w){
to[tot]=v;
ne[tot]=head[u];
cost[tot]=w;
head[u]=tot++;
}
int ans;
int dp[N];
void dfs(int fa,int u,int zhi){
int ge=0;dp[u]=0;
for(int i=head[u];~i;i=ne[i]){
int v=to[i],w=cost[i];
if(v==fa) continue;
ge++;
dfs(u,v,zhi);
if(w>zhi){
dp[u]+=dp[v];
}
else{
dp[u]+=min(dp[v],w);
}
}
if(ge==0){
dp[u]=m+1;
}
}
int pan(int zhi){
dfs(0,1,zhi);
//cout<<dp[1]<<endl;
return dp[1]>m;
}
int main(){
while(~scanf("%d%d",&n,&m)){
if(!n&&!m)return 0;
init();
int l=1,r=m;
for(int i=0;i<n-1;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
int an=inf;
while(l<=r){
int mid=l+r>>1;
if(!pan(mid)){
r=mid-1;an=mid;
}
else{
l=mid+1;
}
}
//cout<<l<<" "<<r<<endl;
if(an==inf){
printf("-1\n");
}
else{
printf("%d\n",an);
}
}
return 0;
}
/*
5 5
1 3 5
1 4 5
3 5 5
4 2 5
5 5
1 3 6
1 4 6
3 5 6
4 2 6
*/
🎈Magic boy Bi Luo with his excited tree
题意
每个节点有一个价值Vi,每走一次一条边要花费Ci,问从各个节点出发(可回来,可不回来)最多能收获多少价值
3
11 91 91
1 2 1
1 3 5
比如这数据,1点收获11+91+91-1*2-5=186
2点收获11+91+91-1-5=187(3点同下)
思路
copy别人的思路,自己菜:先up to down搜索
求出从该节点历经子节点再回来的最大价值
不用回到该节点的最大价值以及此时停在哪一颗子节点上
不用回到该节点的次大价值
然后二搜,计算出子节点up能回来的最大价值以及不用回来的最大价值
显然结果就是max(down再回来的最大价值+up不用回来的最大价值,down不用回来的最大价值+up再回来的最大价值)。
这里就需要判断是否是这个点到子节点最大的,如果不是就选次大
AC代码
#include <bits/stdc++.h>
#define endl '\n'
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS ios::sync_with_stdio(false)
#define ll long long
#define mod 1000000007
#define inf 0x3f3f3f3f
using namespace std;
const int N=1e5+10;
int n,m;
int head[N],to[N<<1],ne[N<<1],cost[N<<1];
int tot;
int a[N];
int id[N];//最后从哪哥点来的
int ans[N];
void init(){
mem(head,-1);
mem(ans,0);
mem(id,-1);
tot=0;
}
void add(int u,int v,int w){
to[tot]=v;
ne[tot]=head[u];
cost[tot]=w;
head[u]=tot++;
}
int dp[N][3];//dp[][0]回到自身 dp[][1]没有回到自身 dp[][2]次优
void dfs(int fa,int u){
dp[u][0]=a[u];dp[u][1]=a[u];dp[u][2]=a[u];//cout<<u<<endl;
for(int i=head[u];~i;i=ne[i]){
int v=to[i],w=cost[i];
if(v==fa) continue;
dfs(u,v);
int tmep=max(0,dp[v][0]-2*w);
dp[u][1]+=tmep;
dp[u][2]+=tmep;
if(dp[u][0]+dp[v][1]-w>dp[u][1]){
dp[u][2]=dp[u][1];
dp[u][1]=dp[u][0]+dp[v][1]-w;
id[u]=v;
}
else if(dp[u][0]+dp[v][1]-w>dp[u][2]){
dp[u][2]=dp[u][0]+dp[v][1]-w;
}
dp[u][0]+=tmep;
}
}
void dfs2(int u,int fa,int one,int two){
ans[u]=max(dp[u][0]+two,dp[u][1]+one);
dp[u][1]+=one;
dp[u][2]+=one;
if(dp[u][1]<=dp[u][0]+two){
dp[u][1]=dp[u][0]+two;
id[u]=fa;
}
else if(dp[u][2]<=dp[u][0]+two){
dp[u][2]=dp[u][0]+two;
}
dp[u][0]+=one;
for(int i=head[u];~i;i=ne[i]){
int v=to[i];
int c=cost[i];
if(v==fa)continue;
int tmep=max(0,dp[u][0]-2*c-max(0,dp[v][0]-2*c));
int tm2;
if(id[u]==v){
tm2=max(0,dp[u][2]-c-max(0,dp[v][0]-2*c));
}
else{
tm2=max(0,dp[u][1]-c-max(0,dp[v][0]-2*c));
}
// cout<<tmep<<" "<<tm2<<endl;
dfs2(v,u,tmep,tm2);
}
}
int main(){
int t;scanf("%d",&t);int ca=0;
while(t--){
scanf("%d",&n);
init();
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=0;i<n-1;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
printf("Case #%d:\n",++ca);
dfs(0,1);
//for(int i=1;i<=n;i++){printf("%d\n",ans[i]);}
dfs2(1,0,0,0);
for(int i=1;i<=n;i++){
printf("%d\n",ans[i]);
}
}
return 0;
}
/*
2
2
11 91
1 2 87
5
4 1 7 7 7
1 2 6
1 3 1
2 4 8
3 5 2
*/