P1041 传染病控制(noip2003)(搜索)
呃呃呃。。。真的是惨烈啊。。。
今天的模拟赛是真的惨。。。。。
本题,正解居然是搜索!!!!!!
蒟蒻自己歪歪了一个貌似是正解但是却连一半都没过的错解。
先解释一下自己的dp思路把。
$f[i][u][v][0/1]$表示第i秒(等效于深度)uv之间的边断还是不断。
转移有两个:
$f[dep[u]][u][v][1]=size[v];$表示此边断,然后下面的点全对答案有贡献
然后找到v的儿子
$f[dep[u]][u][v][0]=max(f[dep[u]][u][v][0],max(f[dep[v]][u][j][0],f[dep[v]][u][j][1]));$
临收卷前发现貌似第二维可以压掉,也就是:$f[dep[v]][v][0/1]$表示v上面那个边要不要删。
然后发现随手hack....
于是,我发现答案统计的时候,会有惊人的重复计算。
于是,我歪歪了好久,怎么标记统计过了,怎么返回,怎么....
然后....发现最后好像被我打成了贪心???
我在每一层去最大值,然后标记,返回,从别的子树里找最大值,然后一直重复到底层。
于是...我抱着能A掉这题的心情,笑了起来。。。
然后我rand了好多的的数据,都没有hack掉,我更开心了。
(先放dp代码:)
#include<bits/stdc++.h>
#define g(x) for(int i=head[x];i;i=e[i].next)
#define l(x) for(int l=head[x];l;i=e[l].next)
using namespace std;
const int maxn=505;
int n,p;
struct edge
{
int to,next;
}e[maxn];
int head[maxn],cnt;
void addedge(int from,int to)
{
e[++cnt]=(edge){to,head[from]};
head[from]=cnt;
}
int dep[maxn];
int size[maxn];
int fa[maxn];
void dfs(int u,int f)
{
dep[u]=dep[f]+1;
size[u]=1;
fa[u]=f;
g(u)
{
int v=e[i].to;
if(v==f)
continue;
dfs(v,u);
size[u]+=size[v];
}
}
int f[maxn][maxn][2];
int mp;
void dp(int u)
{
g(u)
{
int v=e[i].to;
if(v==fa[u])
continue;
dp(v);
f[dep[u]][u][v][1]=size[v];
for(int j=1;j<=n;j++)
if(fa[j]==v)
f[dep[u]][v][0]=max(f[dep[u]][v][0],max(f[dep[v]][j][0],f[dep[v]][j][1]));
}
}
int ans;
int vis[maxn];
void getans(int u)
{
int m=0;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa[u])
continue;
m=max(m,max(f[dep[u]][v][1],f[dep[u]][v][0]));
}
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa[u])
continue;
if(m==f[dep[u]][v][1]&&vis[u]==0)
{
ans+=m;
vis[u]=1;
continue;
}
else
{
getans(v);
}
}
}
int main()
{
scanf("%d%d",&n,&p);
for(int i=1;i<=p;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
addedge(0,1);
dfs(1,0);
dp(1);
for(int i=1;i<=n;i++)
mp=max(mp,dep[i]);
getans(1);
printf("%d",n-ans);
return 0;
}
dp错在于:它会漏状态,也就是说:
我的dp是从子树开始更新,然鹅,即使我在答案统计上避免了重复,它也从一个dp变成了一个贪心,也就是说,它貌似是等效于每次在一层之内找一个子树最大的点加上,这个贪心是错的。
从而得出:dp都是错的。
因为它总会漏状态,如果从儿子更新,同深度的状态没法记录,如果从孙子更新,它的曾孙子没法更新。总而言之就是没法更新完全,也就是有后效性。
所以,如果想得出正解,就得从最下开始更新子树,而这点dp是没法做到的,所以,能够猜到正解是什么:
暴力
怎么暴力怎么写,直接更新,不需要记录状态。
这个复杂度简直玄学。。。
如果是分叉很多,这个dfs能被卡成皮皮,但是这题的数据貌似非常水....
所以,暴力的思路就是:
记录每个深度的所有点,然后对于这些同深度的点进行更新,找出最大值,一次一次向上更新,一直到根节点。
感觉这个深搜巨tm像广搜啊....但是....好吧我错了
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 505; int n, p; vector<int> g[maxn], dep[maxn]; int vis[maxn]; int ans, s; void dfs(int u, int d) { dep[d].push_back(u); for (int i = 0; i < g[u].size(); i++) { dfs(g[u][i], d + 1); } } int work(int u, int t) { int ss = 1; vis[u] = t; for (int i = 0; i < g[u].size(); i++) { ss += work(g[u][i], t); } return ss; } void getans(int d) { for (int i = 0; i < dep[d].size(); i++) { if (vis[dep[d][i]]) continue; s += work(dep[d][i], 1); ans = max(s, ans); getans(d + 1); s -= work(dep[d][i], 0); } } int main() { scanf("%d%d", &n, &p); for (int i = 1; i <= p; i++) { int x, y; scanf("%d%d", &x, &y); if (x > y) swap(x, y); g[x].push_back(y); } dfs(1, 1); getans(2); printf("%d", n - ans); return 0; }