POJ3345

时间:2023-03-08 18:48:42

http://poj.org/problem?id=3345

大意:

大意是说现在有n个城市来给你投票,你需要至少拿到m个城市的赞成票。想要获得第i个城市的赞成需要花费w[i],有个条件就是某些城市是在其他某个城市的统治下的,当获得某个城市的赞成票后,那么他所统治的其他城市都会投票给你(不再需要花费),球最小花费

题解:

我们很容易看出这个统治关系组成了一个树的结构,就等于拿到i整个子树的所有节点的花费就是节点i的花费,求最后拿到>=m个节点的最小花费

我的状态也是按照题目要求设的,F[i]表示到目前为止得到i个城市的赞成的最小花费,然后进行树上DP。

F[i] = min{F[i-num[u]] + w[u]}

其中u表示当前所处的节点,num[u]表示节点u子树上节点数量

这里这样DP是会有问题的,就是我们不能在子节点的F值算出来后再算父节点的F值,否则就可能存在用子节点选择了的状态更新父节点选择的状态,也就是说在DFS时,只能先算父节点的F值,再进行dfs算子节点的

另外这里的F[i-num[u]]实际上只能是由节点u的已经算出的兄弟节点推到(就是说在考虑节点u加入时,他的父节点是不能被加入了的)

上面两个要求统一起来就是说计算父节点的F值(吧父节点加入),子节点的不可以先被计算出;计算子节点的F值时,父节点不可以先加入。解决办法就是另外开一个数组G[i][j],用来临时存放节点i往下递归前的结果,回溯时再更新

上面的F,G对应下面的DP,DP2

dfs1用来计算num数组,dfs2是DP过程,复杂度O(n^2)

 #include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 1e9
#define inf (-((LL)1<<40))
#define lson k<<1, L, mid
#define rson k<<1|1, mid+1, R
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
#define FOPENIN(IN) freopen(IN, "r", stdin)
#define FOPENOUT(OUT) freopen(OUT, "w", stdout)
#define rep(i, a, b) for(int i = a; i <= b; i ++)
template<class T> T CMP_MIN(T a, T b) { return a < b; }
template<class T> T CMP_MAX(T a, T b) { return a > b; }
template<class T> T MAX(T a, T b) { return a > b ? a : b; }
template<class T> T MIN(T a, T b) { return a < b ? a : b; }
template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b; } typedef __int64 LL;
//typedef long long LL;
const int MAXN = ;
const int MAXM = ;
const double eps = 1e-;
const LL MOD = ; bool G[][];
map<string, int>M;
int cnt, w[], dp[], dp2[][], n, m;
char str[], s[];
int num[], tot; int get_index(char* s)
{
if(M[s]) return M[s];
return M[s] = ++cnt;
} void init()
{
int x, no;
cnt = ; M.clear();tot = ;
mem0(G); mem0(dp); mem0(num);
sscanf(s, "%d %d", &n, &m);
for(int i = ; i <= n; i ++) G[][i] = ;
for(int i = ; i <= n; i ++)
{
gets(s);
sscanf(s,"%s %d%*c", str, &x);
if( !M[str] ) M[str] = ++cnt;
w[no = M[str]] = x;
int len = strlen(s), p = len - , index = ;
while( !isdigit(s[p]) ) p --; p ++;
if(p == len) continue;
p++;
while(p <= len)
{
if(p == len || s[p] == ' ')
{
str[index] = ;
int id = get_index(str);
G[no][id] = ;
G[][id] = ;
index = ;
}
else str[index++] = s[p];
++p;
}
}
} void dfs1(int u)
{
dp[u] = INF;
num[u] = ;
for( int i = ; i <= n; i ++) if( G[u][i] )
{
dfs1(i);
num[u] += num[i];
}
dp[] = ;
} void dfs2(int u)
{
memcpy(dp2[u], dp, sizeof(dp));
if(u) for(int i = tot; i >= ; i --)
{
dp2[u][i + num[u]] = min(dp[i + num[u]], dp[i] + w[u]);
}
for(int i = ; i <= n; i ++) if(G[u][i])
dfs2(i);
for(int i = ; i <= n; i ++)
dp[i] = min(dp[i], dp2[u][i]);
tot ++;
} void print()
{
int ans = INF;
for(int i = m; i <= n; i ++)
ans = min(ans, dp[i]);
printf("%d\n", ans);
} int main()
{
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
while(gets(s) && s[] != '#')
{
init();
dfs1();
dfs2();
print();
}
return ;
}