NOIP2003提高组总结+反思

T1:神经网络

考察知识:图的基本知识,细节

算法难度:XX+ 实现难度:XX+ 

分析:

这道题不难,但是细节有点多(见反思)

我们考虑节点时需要使用上一个节点的值(所以要反向建图),所以可以用函数的递归调用实现

转移方程:C_i=\sum_{ji\epsilon E}^{ }C_j\times W_j_i - U_i

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
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;
}

反思:

题目部分截图:

扫描二维码关注公众号,回复: 2654002 查看本文章

没有搞清楚题意!当场100-->20,惨不忍睹,T1差点爆零

我先解释一下图片表达的信息:

图一:说明神经元没有处于兴奋状态,它就不会向其他神经元传送信号(-40)

图二:只输出出度为0且满足Ci<0的点(-80)

情况三:考虑只有一个点的情况(-20)

然而我忽略了图一的信息,没有看见图二的要求,没有考虑情况三,于是错(W)误(A)百出,T_T

T2:

T3:加分二叉树

考察知识:二叉树的遍历序列型动态规划

算法难度:XX+ 实现难度:XXX

分析:

考察二叉树的遍历:

中序遍历先遍历左子树然后当前根节点再然后右子树

前序遍历先遍历当前根节点然后左子树再然后右子树

二叉树的遍历是递归定义的,所以我们可以递归解决(加记忆化节省时间)

而这道题求最大分数,用动态规划解决:

定义状态方程:f(i,j)表示中序遍历序列为i到j的二叉树的最大得分

状态转移方程:f(i,j)=f(i,k-1)\times f(k+1,j)||k\epsilon (i,j)

细节见代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
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;
}

T4:

猜你喜欢

转载自blog.csdn.net/Hi_KER/article/details/81459363