hdu1054 Strategic Game(贪心,树形dp,二分匹配)
2017-07-13 20:35
429 查看
题意:鲍勃喜欢玩战略游戏。现在有n个城市,他们构成了一棵树。鲍勃可以在某些城市派一个士兵守护,该士兵可以瞭望到所有与该城市相连的边。问鲍勃最少要派遣多少个士兵,才能把所有的边都瞭望到。首先是树形dp的解法:我们可以这样定义状态f[i]表示
以i为子树的根所需建的最少的塔数
有两种状态f[i][0]表示不建塔,f[i][1]表示建塔
那么可以很显然地得到:
如果i不建塔,那么i的儿子j必须建塔,于是f[i][0]=sum(f[i][1])
如果i建塔,那么i的儿子j可建可不建,于是f[i][1]=min(f[j][0],f[j][1])+1;
通过递归自下而上来实现,代码如下:
#include<iostream> #include<cmath> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> const int MAXN=10000; using namespace std; int k,num,n,a,b,cnt,fir[MAXN+5],F[MAXN+5][2]; struct node{int e,next;}h[(MAXN<<1)+5]; struct node1{ int ch,next; }tree[MAXN+5]; bool vis[MAXN+5]; inline void Clear(){ memset(h,0,sizeof h); memset(fir,0,sizeof fir); cnt=0; memset(F,0,sizeof F); memset(tree,0,sizeof tree); memset(vis,0,sizeof 4000 vis); } void dfs(int s){ vis[s]=1; for(int i=fir[s];i;i=h[i].next) { if(!vis[h[i].e]) { tree[h[i].e].next=tree[s].ch; tree[s].ch=h[i].e; dfs(h[i].e); } } } int tree_dp(int root,int flag){ if(F[root][flag]>-1) return F[root][flag]; if(!tree[root].ch) { if(flag) return F[root][flag]=1; else return F[root][flag]=0; } if(flag) { int sum=1; for(int i=tree[root].ch;i;i=tree[i].next) sum+=min(tree_dp(i,0),tree_dp(i,1)); return F[root][flag]=sum; } else { int sum=0; for(int i=tree[root].ch;i;i=tree[i].next) sum+=tree_dp(i,1); return F[root][flag]=sum; } } inline void Read(int &Ret){ char ch;bool flag=0; for(;ch=getchar(),ch<'0'||ch>'9';) if(ch=='-') flag=1; for(Ret=ch-'0';ch=getchar(),'0'<=ch&&ch<='9';Ret=Ret*10+ch-'0'); flag&&(Ret=-Ret); } int main() { while(scanf("%d",&n)!=EOF) { Clear(); for(int i=1;i<=n;i++) { scanf("%d",&a); getchar(); getchar(); scanf("%d",&k); a++; getchar(); for(int j=1;j<=k;j++) { scanf("%d",&b); b++; F[a][0]=F[a][1]=F[b][0]=F[b][1]=-1; h[++cnt].e=a; h[cnt].next=fir[b]; fir[b]=cnt; h[++cnt].e=b; h[cnt].next=fir[a]; fir[a]=cnt; } } int s=1; dfs(s); tree_dp(s,1); tree_dp(s,0); printf("%d\n",min(F[s][0],F[s][1])); } }然后是贪心解法:我总觉得贪心解法有点神奇,但是又是如此的有道理,贪心策略是:对于图中度数为1的点来说,在它上面放士兵很显然是并不优的,但是呢,对于其相接的那个点,放士兵一定是最优的,这个是比较显然的,不证了。所以说,方法就是,通过度数为1的节点,删除节点,又再在删去节点后的树中继续找度为1的节点,不断循环,直到不存在度为1的节点即可代码:(当时没想出来,参考了一下网上的代码,跑得比树形dp快)#include<iostream>#include<cstdlib>#include<cstdio>#include<cmath>#include<cstring>#include<vector>#include<algorithm>#include<queue>const int MAXN=1505;using namespace std;int edge[MAXN]; queue<int> Q;vector<int> tree[MAXN];int ans,N,a,b,k; bool vis[MAXN];int main(){while(scanf("%d",&N)!=EOF){memset(vis,0,sizeof vis);ans=0;memset(edge,0,sizeof edge);for(int i=1;i<=N;i++){scanf("%d:(%d)",&a,&k);a++;for(int j=1;j<=k;j++){scanf("%d",&b); b++;tree[a].push_back(b);tree[b].push_back(a);edge[a]++; edge[b]++;}}if(N==1){putchar('1'); putchar(10); continue;}for(int i=1;i<=N;i++)if(tree[i].size()==1) Q.push(i);while(!Q.empty()){int s=Q.front(); Q.pop();int e=tree[s][0]; //对于这个决策,肯定是选e更优if(vis[e]) continue;ans++; vis[e]=1;int len=tree[e].size();for(int i=0;i<len;i++){edge[tree[e][i]]--;if(vis[tree[e][i]]) continue;if(edge[tree[e][i]]==1)Q.push(tree[e][i]);}}printf("%d\n",ans);for(int i=1;i<=N;i++) tree[i].clear();}}二分匹配的解法容我再想想。。。
相关文章推荐
- LA 2038 Strategic game(最小点覆盖,树形dp,二分匹配)
- 【贪心】【树形DP】[POJ1463][HDU1054]Strategic game 战略游戏
- hdu1054 【树形dp】【二分匹配】
- HDU1054 Strategic Game(二分匹配)
- HDU 1054 Strategic Game (树形DP/匹配)
- 二分匹配HDU1054 Strategic Game
- bzoj 2067: [Poi2004]SZN【贪心+二分+树形dp】
- BZOJ 2067 POI 2004 SZN 树形DP 贪心 二分答案
- [BZOJ2097][Usaco2010 Dec]Exercise 奶牛健美操(二分+树形dp+贪心)
- HDU 1054 Strategic Game 二分匹配 | 树型DP | 贪心
- BZOJ 2097 USACO 2010 Dec Gold Exercise 奶牛健美操 二分答案 树形DP 贪心
- HDU1054 Strategic Game(树形dp)
- hdu1054 Strategic Game(树形DP)
- [BZOJ2097][Usaco2010 Dec]Exercise 奶牛健美操(二分+树形dp+贪心)
- hdu1054 Strategic Game(树形dp)
- ZOJ1134 POJ1463 HDU1054 Strategic Game, 树形DP
- 【bzoj2097】[Usaco2010 Dec]Exercise 奶牛健美操 二分答案+树形dp+贪心
- poj1463--hdu1054--Strategic Game(树形DP练习4)
- poj 1463 Strategic game(树形DP)
- HDU 3586 Information Disturbing(二分+树形dp)