【NOIP2003】提高组
T1神经网络
这道题是第一题,把它放在签到题的位置就不用想太多。听说可以用拓扑排序,但是实际上由于数据比较水,裸的bfs即可AC。
注意几点:输入层的ci[i]不需要减去ui[i];只有当ci[i]>0时才能传递给下一层;只需要输出ci[i]>0的输出层。
剩下的细节实现看代码:
#include<cstdio> #include<cstring> const int maxn=205; using namespace std; int ci[maxn],ui[maxn],q1[maxn],q2[maxn],a1=0,a2=0; int u,v[maxn],s[maxn],first[maxn],next[maxn]; bool ok[maxn]; int read() { int ans=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();} return ans*f; } void bfs() { int head=0,to; while(head<a1) { int x=q1[++head]; for(int i=first[x];i>0;i=next[i]) { to=v[i]; ci[to]+=s[i]*ci[x]; if(ok[to]&&ci[to]>0)q1[++a1]=to,ok[to]=0; } } } int main() { memset(first,-1,sizeof(first)); int n=read(),p=read(); for(int i=1;i<=n;i++) { scanf("%d %d",&ci[i],&ui[i]); if(ci[i]>0)q1[++a1]=i; else ci[i]-=ui[i]; } for(int i=1;i<=p;i++) { scanf("%d %d %d",&u,&v[i],&s[i]); next[i]=first[u];first[u]=i; ok[u]=1; } for(int i=1;i<=n;i++)if(!ok[i])q2[++a2]=i; bfs();bool f=0; for(int i=1;i<=a2;i++) { int x=q2[i]; if(ci[x]>0) { f=1;printf("%d %d\n",x,ci[x]); } } if(!f)printf("NULL"); return 0; }
T2侦探推理
这道题是字符串的模拟题,很恶心......我就不写了,有兴趣的自行跳转到此链接
T3加分二叉树
这道题就是一个套在树上的dp,枚举每一个点为根节点的情况,递归更新答案即可。
注:p[l][r]表示[l~r]这一段的根节点。
具体细节看代码:
#include<cstdio> #include<cstring> const int maxn=35; typedef long long LL; using namespace std; LL ans=0,f[maxn][maxn],w[maxn]; int n,p[maxn][maxn]; int read() { int ans=0,f1=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f1=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();} return ans*f1; } LL dp(int l,int r) { if(l>r)return 1; if(f[l][r]!=-1)return f[l][r]; if(l==r){p[l][r]=l;return w[l];} for(int i=l;i<=r;i++) { LL x=dp(l,i-1)*dp(i+1,r)+w[i]; if(x>f[l][r]) { f[l][r]=x; p[l][r]=i; } } return f[l][r]; } void print(int l,int r) { int x=p[l][r]; printf("%d ",x); if(l<x)print(l,p[l][r]-1); if(r>x)print(p[l][r]+1,r); } int main() { memset(f,-1,sizeof(f)); n=read(); for(int i=1;i<=n;i++) w[i]=read(); ans=dp(1,n); printf("%lld\n",ans); print(1,n); return 0; }
T4传染病控制
这道题告诉了我们它是一棵树,因此可以直接连一条有向边。
刚开始自己的DFS总是写炸,但是有些数据是真的水错误的程序都能水过6个点2333。
最后参考了一下某位dalao的代码才恍然大悟,我在细节处理上总是差那么一点点(还是要努力啊)。
DFS的话枚举当前到下一步能走到的所有点中被断绝联系的那一个点,然后再将除了这个点外其他点的子节点都存进son数组里,如果存完son数组仍为空,则此时已经控制住,可以尝试更新答案。
还可以加一个小小的剪枝:如果当前情况的num已经大于我们目前的最小值,则这个答案肯定不是最优解,不必继续往下搜,直接跳出。
剩下的直接递归DFS即可(不得不说函数传一整个数组真是方便get√)。
具体细节还是看代码:
#include<cstdio> #include<iostream> #include<cstring> const int maxn=310,inf=0x3f3f3f3f; using namespace std; int n,p,u,v,to[maxn],tot=0,mni=inf; int first[maxn],xia[maxn]; int read() { int ans=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();} return ans*f; } void insert() { tot++;to[tot]=v;xia[tot]=first[u];first[u]=tot; } void dfs(int tr[maxn],int sz,int num,int d) { int son[maxn],td=0; for(int i=1;i<=sz;i++) { int x=tr[i]; if(x==d)continue; for(int j=first[x];j>0;j=xia[j]) son[++td]=to[j]; } if(!td)mni=min(mni,num); else{ if(num+td-1>=mni)return; for(int i=1;i<=td;i++) dfs(son,td,num+td-1,son[i]); } } int main() { memset(first,-1,sizeof(first)); int son[maxn]; n=read();p=read();int td=0; for(int i=1;i<=p;i++) { u=read();v=read(); if(u>v)swap(u,v); insert(); if(u==1)son[++td]=v; } for(int i=1;i<=td;i++) dfs(son,td,td,son[i]); printf("%d",mni); return 0; }