所谓状态机背包:
- 一个背包,有n种状态。状态k,对应背包中装入了[ai_k,bi_k]个物品i。
- 那么状态之间切换:状态k=》装入物品i=》状态j。
- 注意,状态之间甚至可以互相包含。
- 则一般的状态递推过程:
- for 物品槽i=1 to weight:
- for 状态s=1 to n:
- for 物品obj=1 to m:
- num[s]=>obj=>num[s1] //装包过程是一次状态切换,同时也是一次动规DP
递推方程:
下面的递推方程:
物品抽象为:[x,y]或[x],x为是否带车进入点root, y为是否带车返回到点root, [0,0]为不带车进入root,并不带车退回到root。
state[1]: 背包中仅装入[0,0]和一个以内的[0]
state[2]: 背包中仅装入[0,0]
state[3]: 背包中装入[0,0] [1,1];一个以内的[0]或[1,0], 抑或一个以内的[1]
state[4]: 背包中装入[0,0] [1,1];一个以内的[1,0]
state[5]: 背包中装入[0,0][1,1];
state[6]: 背包中装入[0,0][1,1];一个以内的[0]
state[1~6]对应下面代码里的x1~x6,y1~y6,z1~z6
#include<iostream>
#include <stdio.h>
#include <vector>
#include <map>
#define MAXN 1000000
#define mpr(a,b,c) make_pair(a,make_pair(b,c))
#define cmin(a,b) if(a>b) a=b;
using namespace std;
typedef long long int ll;
typedef pair<int,pair<int,int> > Pair;
ll num[MAXN][6];
inline ll min4(ll a,ll b,ll c,ll d) {
ll tmp=(a<b?a:b);
tmp=(tmp<c?tmp:c);
tmp=(tmp<d?tmp:d);
return tmp;
}
void dp(vector<vector<Pair> > &tree,ll num[][6],int fa,int root){
//[people back][car in][car back]
//car in means car start at root, car back means car stop at root
//
ll y1=0,y2=0,y3=0,y4=0,y5=0,y6=0;
for(int i=0;i<tree[root].size();i++){
Pair &p=tree[root][i];
int son=p.first,c0=p.second.first,c1=p.second.second;
if(son==fa||son==-1) continue;
dp(tree,num,root,son);
ll x1,x2,x3,x4,x5,x6;
//[people][car in][car back]
ll z1=num[son][0];//[0][0][0]
ll z2=num[son][1];//[1][0][0]
ll z3=num[son][2];//[0][1][0]
ll z4=num[son][3];//[1][1][0]
ll z5=num[son][4];//]1][1][1]
x1=min(
y1+(z2+2*c0),
y2+(z1+c0)
);
x2=y2+z2+2*c0;
//state 4,5,6 working for state 3
x4=min(
y4+min(z2+2*c0,z5+2*c1),
y5+(z4+c1+c0)
);
x5=y5+min(z2+2*c0,z5+2*c1);
x6=min(
y6+min(z2+2*c0,z5+2*c1),
y5+(z1+c0)
);
x3=min4(
y3+min(z2+2*c0,z5+2*c1),
y4+(z1+c0),
y5+(z3+c1),
y6+(z4+c1+c0)
);
y1=x1; y2=x2; y3=x3; y4=x4; y5=x5; y6=x6;
}
num[root][0]=y1;
num[root][1]=y2;
num[root][2]=y3;
num[root][3]=y4;
num[root][4]=y5;
}
bool dfsCut(int fa,int root, vector<vector<Pair> > &tree,vector<bool> &nodes){
bool cut=nodes[root];
for(int i=0;i<tree[root].size();i++){
Pair &p=tree[root][i];
int son=p.first;
if(son==fa) continue;
if(!dfsCut(root,son,tree,nodes))
p.first=-1;//cut this
else cut=true;
}
return cut;
}
int main(){
int n;scanf("%d",&n);
vector<vector<Pair> > tree(n,vector<Pair>());
for(int i=0;i<n-1;i++){
int a,b,c0,c1;scanf("%d%d%d%d",&a,&b,&c0,&c1);a--,b--;
tree[a].push_back(mpr(b,c0,c1));
tree[b].push_back(mpr(a,c0,c1));
}
int m;scanf("%d",&m);
vector<bool> nodes(n,false);
for(int i=0;i<m;i++){
int a;scanf("%d",&a);a--;
nodes[a]=true;
}
dfsCut(-1,0,tree,nodes);
int root=0;
dp(tree,num,-1,root);
printf("%d\n",num[root][2]);
return 0;
}
旧版本代码存在两个问题:
- 1 最后输出printf("%d\n",num[root][2]),打印语句会有int溢出 printf int overflow, should be "%lld\n"!!!
- 2 没必要把states存在num[][]里面,直接把states通过dp/dfs()返回就好了!因为本题的递推方程一次只用到了一个子节点,所以没必要记忆所有states到[]
- void dfs(env[],&results[],¤t_result[],current_idx,&maxV) //不返回类型dfs
- 或者 res_type dfs(env[], current_idx, &maxV) //返回类型dfs
#include<iostream>
#include <stdio.h>
#include <vector>
#include <map>
#define MAXN 1000000
#define mpr(a,b,c) make_pair(a,make_pair(b,c))
#define cmin(a,b) if(a>b) a=b;
using namespace std;
typedef long long int ll;
typedef pair<int,pair<int,int> > Pair;
inline ll min4(ll a,ll b,ll c,ll d) {
ll tmp=(a<b?a:b);
tmp=(tmp<c?tmp:c);
tmp=(tmp<d?tmp:d);
return tmp;
}
struct state{
ll z1,z2,z3,z4,z5;
state(ll a,ll b,ll c,ll d,ll e):z1(a),z2(b),z3(c),z4(d),z5(e){}
};
state dp(vector<vector<Pair> > &tree,int fa,int root){
//[people back][car in][car back]
//car in means car start at root, car back means car stop at root
ll y1=0,y2=0,y3=0,y4=0,y5=0,y6=0;
for(int i=0;i<tree[root].size();i++){
Pair &p=tree[root][i];
int son=p.first,c0=p.second.first,c1=p.second.second;
if(son==fa||son==-1) continue;
state s=dp(tree,root,son);
//[people][car in][car back]
ll x1,x2,x3,x4,x5,x6;
ll z1=s.z1,z2=s.z2,z3=s.z3,z4=s.z4,z5=s.z5;
x1=min(
y1+(z2+2*c0),
y2+(z1+c0)
);
x2=y2+z2+2*c0;
//state 4,5,6 working for state 3
x4=min(
y4+min(z2+2*c0,z5+2*c1),
y5+(z4+c1+c0)
);
x5=y5+min(z2+2*c0,z5+2*c1);
x6=min(
y6+min(z2+2*c0,z5+2*c1),
y5+(z1+c0)
);
x3=min4(
y3+min(z2+2*c0,z5+2*c1),
y4+(z1+c0),
y5+(z3+c1),
y6+(z4+c1+c0)
);
y1=x1; y2=x2; y3=x3; y4=x4; y5=x5; y6=x6;
}
return state(y1,y2,y3,y4,y5);
}
bool dfsCut(int fa,int root, vector<vector<Pair> > &tree,vector<bool> &nodes){
bool cut=nodes[root];
for(int i=0;i<tree[root].size();i++){
Pair &p=tree[root][i];
int son=p.first;
if(son==fa) continue;
if(!dfsCut(root,son,tree,nodes))
p.first=-1;//cut this
else cut=true;
}
return cut;
}
int main(){
int n;scanf("%d",&n);
vector<vector<Pair> > tree(n,vector<Pair>());
for(int i=0;i<n-1;i++){
int a,b,c0,c1;scanf("%d%d%d%d",&a,&b,&c0,&c1);a--,b--;
tree[a].push_back(mpr(b,c0,c1));
tree[b].push_back(mpr(a,c0,c1));
}
int m;scanf("%d",&m);
vector<bool> nodes(n,false);
for(int i=0;i<m;i++){
int a;scanf("%d",&a);a--;
nodes[a]=true;
}
dfsCut(-1,0,tree,nodes);
int root=0;
state s=dp(tree,-1,root);
printf("%lld\n",s.z3);//@error: "%d\n"
return 0;
}