CSP/NOIP新赛制内部挑战赛1 C. 生存
sub1
由于一个只有一个幸存者,直接dfs即可
时间复杂度:O(2^N)
期望得分;20pts
注意:考虑停留原地不动的情况! WA了一发
代码
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+5; int n,m,head[maxn],cnt,a[maxn]; vector <int> ds[maxn]; struct edge { int to,nxt; }e[maxn<<1]; void add(int x,int y) { e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt; } int ans=0; void dfs(int x,int step,int num) { if(!num) return; if(step==m) { ans=max(ans,num); return; } int to=x; int flag=0; for(int k=0;k<ds[step+1].size();k++) if(ds[step+1][k]==to) flag=1; dfs(to,step+1,num-flag); for(int i=head[x];i;i=e[i].nxt) { to=e[i].to; flag=0; for(int k=0;k<ds[step+1].size();k++) if(ds[step+1][k]==to) flag=1; dfs(to,step+1,num-flag); } } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); scanf("%d%d",&n,&m); int x,y,pos; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) if(a[i]) pos=i; for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(int i=1;i<=m;i++) { scanf("%d",&x); for(int j=1;j<=x;j++) { scanf("%d",&y); ds[i].push_back(y); } } dfs(pos,0,1); printf("%d",ans); return 0; }
sub2
人与人之间不会互相影响,而且一组的人可以当作总是一起的
考虑dp 设计状态 f[x][i][j]表示第x组人在第i天能否安全到达j
转移即可 时间复杂度 O(N^2*M)
期望得分:33pts
代码
#include<bits/stdc++.h> using namespace std; #define PII pair<int,int> const int maxn=1e6+5; int n,m,head[maxn],cnt,a[maxn]; int dp[205][205]; vector <int> ds[maxn]; struct edge { int to,nxt; }e[maxn<<1]; void add(int x,int y) { e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt; } int ans=0; queue <PII> q; void solve() { for(int k=1;k<=n;k++) { memset(dp,0,sizeof(dp)); q.push(make_pair(0,k)); dp[0][k]=1; while(!q.empty()) { PII u=q.front(); q.pop(); int to=u.second; int tmp=1; for(int j=0;j<ds[u.first+1].size();j++) if(ds[u.first+1][j]==to) tmp=0; if(tmp) { dp[to][u.first+1]=1; if(u.first+1<m) q.push(make_pair(u.first+1,to)); } for(int i=head[u.second];i;i=e[i].nxt) { to=e[i].to; tmp=1; for(int j=0;j<ds[u.first+1].size();j++) if(ds[u.first+1][j]==to) tmp=0; if(!tmp) continue; dp[to][u.first+1]=1; if(u.first+1<m) q.push(make_pair(u.first+1,to)); } } int flag=1; for(int i=1;i<=n;i++) if(dp[i][m]) { flag=0; break; } if(!flag) ans+=a[k]; } printf("%d\n",ans); } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); scanf("%d%d",&n,&m); int x,y; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(int i=1;i<=m;i++) { scanf("%d",&x); for(int j=1;j<=x;j++) { scanf("%d",&y); ds[i].push_back(y); } } solve(); return 0; }
sub3
考虑到上一种dp过程中有很多重复计数
每组人在某一时刻,在某一城市最终能否安全存活是固定的,而我们却反复计算了
所以,换一种状态设计的方式
记录f[i][j]表示第i天在j位置最终能否安全
由于最后一天到达哪个城市都算安全
我们就从时间m-1到1都跑一遍即可
时间复杂度O(NM)
期望得分:52pts
代码
#include<bits/stdc++.h> using namespace std; #define PII pair<int,int> const int maxn=1e6+5; int n,m,head[maxn],cnt,a[maxn]; int dp[3005][3005]; struct edge { int to,nxt; }e[maxn<<1]; void add(int x,int y) { e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt; } int ans=0; void dfs(int t,int u,int fa) { int res=0; if(dp[t+1][u]==1 || dp[t+1][fa]==1) res=1; for(int i=head[u];i;i=e[i].nxt) { int to=e[i].to; if(to==fa) continue; if(dp[t+1][to]==1) res=1; dfs(t,to,u); } if(!dp[t][u]) return; dp[t][u]=res; } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); scanf("%d%d",&n,&m); int x,y; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } memset(dp,-1,sizeof(dp)); for(int i=1;i<=m;i++) { scanf("%d",&x); for(int j=1;j<=x;j++) { scanf("%d",&y); dp[i][y]=0; } } for(int i=1;i<=n;i++) if(dp[m][i]==-1) dp[m][i]=1; for(int i=m-1;i>=0;i--) dfs(i,1,0); for(int i=1;i<=n;i++) ans+=(dp[0][i]==1?a[i]:0); printf("%d",ans); return 0; }
sub4
继续考虑,由于上一种方法的每个点仅仅记录了0/1即为能否生存的状态
所以我们可以考虑把每个点分为黑白,黑色是不能,白色是可以存活
首先,在我们读入完第m天哪些城市没有灾难,这时,我们就得到了一些白点
那么上一种方法中的状态转移就相当于把一些黑点变成了白点
当一个黑点有相邻点是白点,那就可以把它改成白色
那么维护一个黑点相邻的白点有多少个,维护一个队列,存放可能有黑变白的点,在每一天结束的时候,判断哪些点变白
时间复杂度 O(k*max(入度))
期望得分:78pts
由于这个方法和最后的标算很相近了,就不写了
标算
考虑在维护每个点相邻节点入队的时候,时间复杂度带了一个度数
现在就是要考虑把这一部分省略掉
把儿子节点和父亲节点分开
维护白色儿子节点的个数是O(1)的,再记录一个队列,维护的节点是它们的儿子可以由黑变白
在每一天结束的时候,判断哪些点变白,然后重新维护上述两个队列
代码
#include<bits/stdc++.h> using namespace std; #define PII pair<int,int> const int maxn=1e6+5; int n,m,head[maxn],cnt,a[maxn]; vector <int> ds[maxn],sw[maxn],s1,s2; struct edge { int to,nxt; }e[maxn<<1]; void add(int x,int y) { e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt; } int f[maxn],siz[maxn],vis[maxn]; void dfs(int u,int fa) { f[u]=fa; for(int i=head[u];i;i=e[i].nxt) { int to=e[i].to; if(to==fa) continue; dfs(to,u); siz[u]++; } } int calc(int u) { if(f[u]) return vis[f[u]]+siz[u]; return siz[u]; } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); scanf("%d%d",&n,&m); int x,y; for(int i=1;i<=n;i++) scanf("%d",&a[i]),vis[i]=1; for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(int i=1;i<=m;i++) { scanf("%d",&x); for(int j=1;j<=x;j++) { scanf("%d",&y); ds[i].push_back(y); } } dfs(1,0); for(int i=m;i>0;i--) { for(int j=0;j<ds[i].size();j++) { int v=ds[i][j]; if(vis[v]) { vis[v]=0; if(f[v]) { --siz[f[v]]; sw[f[v]].push_back(v); } } } vector <int> cur; for(int j=0;j<ds[i].size();j++) { int v=ds[i][j]; if(!vis[v] && calc(v)) cur.push_back(v); } for(int j=0;j<s1.size();j++) { int v=s1[j]; if(!vis[v] && calc(v)) cur.push_back(v); } for(int j=0;j<s2.size();j++) { int u=s2[j]; if(vis[u]) { for(int k=0;k<sw[u].size();k++) { int v=sw[u][k]; if(!vis[v]) cur.push_back(v); } sw[u].clear(); } } s1.clear(); s2.clear(); for(int j=0;j<cur.size();j++) { int v=cur[j]; if(vis[v]) continue; vis[v]=1; if(f[v]) { ++siz[f[v]]; if(!vis[f[v]]) s1.push_back(f[v]); } s2.push_back(v); } } long long ans=0; for(int i=1;i<=n;i++) if(vis[i]) ans+=a[i]; printf("%lld",ans); return 0; }