考察知识:图的基本知识,细节
算法难度:XX+ 实现难度:XX+
分析:
这道题不难,但是细节有点多(见反思)
我们考虑节点时需要使用上一个节点的值(所以要反向建图),所以可以用函数的递归调用实现
代码:
#include
#include
#include
#include
using namespace std;
const int maxn=105;
#define ll long long
//j->i Ci=sum(Cj*w(j,i))-Ui
struct edge{
int to,next;
ll w;
}e[maxn*50];
int first[maxn],np;
void add(int u,int v,ll w){
e[++np]=(edge){v,first[u],w};
first[u]=np;
}
ll C[maxn],U[maxn];//谨慎一点,用long long
int n,m,cd[maxn];
bool done[maxn];
void dfs(int i){
if(C[i]||done[i]) return;
for(int p=first[i];p;p=e[p].next){
int j=e[p].to;
ll w=e[p].w;
dfs(j);
if(C[j]<0) continue;//注意
C[i]+=C[j]*w;
}
C[i]-=U[i];
done[i]=true;//类似于记忆化搜索
}
int main(){
int u,v;
ll w;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld%lld",C+i,U+i);
for(int i=1;i<=m;i++){
scanf("%d%d%lld",&u,&v,&w);
add(v,u,w);//反向建图
cd[u]++;
}
int cnt=0;
for(int i=1;i<=n;i++) if(!C[i])
dfs(i);
for(int i=1;i<=n;i++) if(C[i]>0&&cd[i]==0){
printf("%d %lld\n",i,C[i]);cnt++;
}
if(!cnt) printf("NULL\n");
return 0;
}
反思:
题目部分截图:
我没有搞清楚题意!当场100-->20,惨不忍睹,T1差点爆零
我先解释一下图片表达的信息:
图一:说明神经元没有处于兴奋状态,它就不会向其他神经元传送信号(-40)
图二:只输出出度为0且满足Ci<0的点(-80)
情况三:考虑只有一个点的情况(-20)
然而我忽略了图一的信息,没有看见图二的要求,没有考虑情况三,于是错(W)误(A)百出,T_T
考察知识:字符串处理,枚举
算法难度:XXX 实现难度:XXXX
分析:面对这道题的题目描述我也是醉了,我觉得题目描述不够严谨,对于某些应该什么的情况没有说明(或者是我分析能力不行)
首先面对这道题的输入,我们应该应该考虑字符串处理(不要考虑gets()函数),但是我们怎么判断换行呢,如果我开始用ch=='\n'
洛谷上只有30分,在vijos80分,原来在洛谷上应该用 '\r' 判断
所以我们手动输入一行,用如下方法:
while((ch=getchar())!='\n'&&ch!='\r') s[p++]=ch;
然后我们怎么处理这些混乱的逻辑关系呢?首先我们判断是否有说话一定矛盾的人,如果有直接输出"Impossible"
没有的话,我们枚举凶手是谁,同时枚举星期几,然后统计说谎的人数,注意:可能有无法判断是否说谎的人,按照数据的答案,我们可以将其看作说谎,也可以不看作说谎,可是题目并没有说明这种情况
好吧,基本思想就是这样,但是具体的代码实现细节很多
我用结构体储存每个人的证词,写自定义函数判断是否说谎,
代码:
#include
#include
#include
#include
#include
#include
#include
考察知识:二叉树的遍历,序列型动态规划
算法难度:XX+ 实现难度:XXX
分析:
考察二叉树的遍历:
中序遍历先遍历左子树然后当前根节点再然后右子树
前序遍历先遍历当前根节点然后左子树再然后右子树
二叉树的遍历是递归定义的,所以我们可以递归解决(加记忆化节省时间)
而这道题求最大分数,用动态规划解决:
定义状态方程:f(i,j)表示中序遍历序列为i到j的二叉树的最大得分
状态转移方程:
细节见代码:
#include
#include
#include
#include
using namespace std;
#define ll long long
const int maxn=35;
ll ans,f[maxn][maxn];
bool vis[maxn][maxn];
int n,a[maxn],root[maxn][maxn];
ll dp(int l,int r){
if(l>r) return 1;
if(l==r) return (ll)a[l];
if(vis[l][r]) return f[l][r];
ll T;
for(int rt=l;rt<=r;rt++){
T=dp(l,rt-1)*dp(rt+1,r)+a[rt];
if(T>f[l][r]) f[l][r]=T,root[l][r]=rt;//记忆路径
}
vis[l][r]=true;
return f[l][r];
}
void out(int l,int r){//输出路径
if(l>r) return;
if(l==r){printf("%d ",l);return;}
int rt=root[l][r];
printf("%d ",rt);//相当于二叉树的前序遍历
out(l,rt-1);
out(rt+1,r);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
printf("%lld\n",dp(1,n));
out(1,n);
return 0;
}
考察知识:树的基本知识,搜索+剪枝
算法难度:XXXX 实现难度:XXX
分析:这道题用不加剪枝的搜索往往都能得50分以上(我得了80分)
但是这道题的搜索方法真的不是非常好想,但想出来这道题也就不难了
单纯用这种方法不加剪枝就可以得80分,那么我们怎么加剪枝呢?
我们预处理每层的最大sz[i],如果搜索到某一层,剩下的所有节点选最大的删除还达不到之前得到的最大值,则剪枝。
剪枝后速度快了进10倍
代码:
#include
#include
#include
using namespace std;
const int maxn=305;
int n,m,g[maxn][maxn],sz_g[maxn];
int fa[maxn],sz[maxn],dep[maxn],ans;
int SUM[maxn];
bool vis[maxn];
void ready(int i,int Fa,int Dep){//预处理
fa[i]=Fa,sz[i]=1,dep[i]=Dep;
for(int p=sz_g[i];p>0;p--){
int j=g[i][p];
if(j!=Fa){
ready(j,i,Dep+1);
sz[i]+=sz[j];
}
}
}
bool check(int i){
while(i) if(vis[i=fa[i]]) return false;
return true;
}
void dfs(int Dep,int sum){
if(sum+SUM[Dep]<=ans) return; //剪枝
ans=max(ans,sum);
for(int i=1;i<=n;i++) if(dep[i]==Dep&&check(i)){
vis[i]=true;
dfs(Dep+1,sum+sz[i]);
vis[i]=false;
}
}
int main(){
int u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
sz_g[u]++,sz_g[v]++;
g[u][sz_g[u]]=v,g[v][sz_g[v]]=u;
}
ready(1,0,0);
for(int Dep=n;Dep>=1;Dep--)//预处理剪枝条件
for(int i=1;i<=n;i++) if(dep[i]==Dep)
SUM[Dep]=max(SUM[Dep],sz[i]);
for(int i=n-1;i>=1;i--) SUM[i]+=SUM[i+1];
dfs(1,0);
printf("%d\n",n-ans);
return 0;
}
总结:
T_T终于做完了,被T2卡了很久,交了10次才AC,其中被输入卡了5次,呜呜呜... ...
2002年没有一道数据结构题,2003年四道有三道根数据结构有关,这个出题的知识点覆盖真的奇怪。。。
2003年的题因为我没有审题(或者语文太差,我语文真的菜T_T),或者考虑不够严谨几乎完了
第一次提交分数:20+10+100+40=170 我真的菜啊T_T。。。。。。