机器人采矿
//对子树进行考虑,都有派m个回n个的可能,但是有冗余 //对于去一个回一个,去m个回m个,第二种肯定耗费多。因为去一个回一个类比dfs每一条边走两次即可(这也是最小的情况),而好几个机器人, //每一条边走两次 ,而且至少+去子树、回子树,(每个机器人独立计算),所以要回来就只派一个 //同样,如果对一棵子树部分机器人返回,部分不返回,则这种情况一定没有仅仅只用不返回的部分遍历整棵子树且不返回小 //也就是说,要么派一个然后回来,要么派好几个然后不回来 //不用枚举左右分别是几个机器人 //有两个子树可以先求一个的最小值,因为最小值是d(i,j),即在ij情况下的最小值,所以第二个子树即可用到这个最小值,但是不能破坏了这个最小值,因为用到j-k, //所以必须逆序循环,否则就会导致先求出了两个都考虑的最小值,然后又把它当做一个的最小值用 #include <bits/stdc++.h> using namespace std; int dp[100000+10][15],n,robots,s,u[200000+10],v[200000+10],w[200000+10],first[100000+10],next1[200000+10]; void dfs(int k,int f){ // cout<<k<<" "; for(int i=first[k];i!=-1;i=next1[i]){ if(f!=v[i]){ dfs(v[i],k);//叶子没子树,所以还是0 for(int m=robots;m>=0;m--){ //机器人个数,这里语法糖 //不用枚举左右分别是几个机器人 //有两个子树可以先求一个的最小值,因为最小值是d(i,j),即在ij情况下的最小值,所以第二个子树即可用到这个最小值 //,但是不能破坏了这个最小值,因为用到j-k, //所以必须逆序循环,否则就会导致先求出了两个都考虑的最小值,然后又把它当做一个的最小值用 dp[k][m]+=dp[v[i]][0]+2*w[i];//用+=其实是第一次=赋值,第二次相加然后min //本质是dp[k][m]=dp[k][m]+dp[v[i]][0]+2*w[i];多考虑一个的dp=本来的dp+一个子树有来有回 //这里相加的真是含义是分配0个,单独拿出来而已 for(int p=1;p<=m;p++){ //最起码派一个,第一次p=m无意义,但是第二次就可以p=m了(第一次派一个然后在回来) dp[k][m]=min(dp[k][m],p*w[i]+dp[k][m-p]+dp[v[i]][p]); //第一边子树时dp[k][m-p]恰好是0,第二遍时就用到了另一边 } //注意这里求的值并不是把机器人都派出去,有可能有留着的,这说明。。。类似于01背包问题,有dp[i][m]=dp[i][m+k] 对于某些k>0 //而在第二次的时候,是恰好剩下m-p个机器人然后dp[k][m-p]是可以的,因为用没用完都可以 } } } } // int DP(int i,int j){ // if(dp[i][j]!=-1) return dp[i][j]; // dp[i][j] // } int main() { memset(dp,0, sizeof(dp)); memset(first,255, sizeof(first)); cin>>n>>s>>robots; for(int i=1;i<=n-1;i++){ cin>>u[i]>>v[i]>>w[i]; u[i+n]=v[i]; v[i+n]=u[i]; w[i+n]=w[i]; next1[i]=first[u[i]]; first[u[i]]=i; next1[i+n]=first[u[i+n]]; first[u[i+n]]=i+n; } dfs(s,-1); // cout<<DP(3,2); cout<<dp[s][robots]<<endl; return 0; }