您的位置:首页 > 其它

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();}}二分匹配的解法容我再想想。。。

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: