我想说的:以前做背包的题目做多了,脑子形成了一种就是所有动态规划就是在数组上进行等一些固定的思想。结果最近在做一些题目的时候,感觉无从下手,想好久都没结果。非常庆幸自己做了这类题目,让我从那种狭隘的思想中做出来。以后就要根据实际情况研究状态了。
题意:有n个城市,每两个城市只有一条路连接,(所以可以当成是一颗树)。现在要建设一些防火站,使每个城市都可以被覆盖。每个城市的属性是:在本城市建防火站的价钱和不在本城市建设的话,所依赖的别的防火站的最长距离。然后是n-1条路,和这条路的长度。现在问在覆盖所有城市的前提下的最少价格。
解题:首先要说的是,感觉这个题目很难,自己想了一天,然后看了一天的解题报告,包括国家集训队论文,陈启峰2006年的《一张一弛,解题之道 ——“约制、放宽”方法在解题中的应用》,结果还是不明白,最后只有对着讲解,看代码,模拟,最后又花费了一上午的时间理解了一个大概,现在就用自己理解的来说一下吧。
dp[i][j]:表示以i为根的子树里修建一些消防站,并在节点j处修建一消防站,i的负责站必须是j;
best[i]:表示表示以i为根的子树的所有节点都有负责站的最小花费。
dist[i]:表示i到x的距离(每换换一个节点,都要再求一次dist[])。
状态状态转移方程:dp[i][j]=w[j]+sum(min(best[y],dp[y][j]-w[i]))(y是i的所有孩子);
哎!不是很理解,所有也就能写这么多了,在网上搜了一个详细的,大家可以参考这个poj 2152 Fire。
个人代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
#define N 1010
#define M (1<<30)
struct City{
int d,w;
}city[N];
struct lianjie{
int child,d;
lianjie(int x,int y)
{
child=x;d=y;
}
};
vector <lianjie> tree[N];
int num,dist[N],best[N],dp[N][N];
void init()
{
scanf("%d",&num);
int i,u,v,l;
for(i=1;i<=num;i++) tree[i].clear();
for(i=1;i<=num;i++) scanf("%d",&city[i].w);
for(i=1;i<=num;i++) scanf("%d",&city[i].d);
for(i=1;i<num;i++)
{
scanf("%d%d%d",&u,&v,&l);
tree[u].push_back(lianjie(v,l));
tree[v].push_back(lianjie(u,l));
}
}
void dis(int key)
{
int length=tree[key].size();
for(int i=0;i<length;i++)
{
int v=tree[key][i].child;
if(dist[v]!=-1) continue;
dist[v]=dist[key]+tree[key][i].d;
dis(v);
}
}
void dfs(int key,int parent)
{
int i,j,v,length=tree[key].size();
for(i=0;i<length;i++)
{
v=tree[key][i].child;
if(v!=parent) dfs(v,key);
}
memset(dist,-1,sizeof(dist));
dist[key]=0; best[key]=M; dis(key);
for(i=1;i<=num;i++)
{
if(dist[i]>city[key].d) dp[key][i]=M;
else
{
dp[key][i]=city[i].w;
for(j=0;j<length;j++)
{
v=tree[key][j].child;
if(v!=parent)
dp[key][i]+=min(best[v],dp[v][i]-city[i].w);
}
best[key]=min(best[key],dp[key][i]);
}
}
}
int main()
{
//freopen("/home/acm/JPY/input.txt","r",stdin);
int T;cin>>T;
while(T--)
{
init(); dfs(1,0);
cout<<best[1]<<endl;
}
return 0;
}