NOIP2014提高组DAY2题解

版权声明:欢迎转载+原文章地址~ https://blog.csdn.net/Hi_KER/article/details/82591776

T1:无线网络发射器选址

考察知识:枚举,二维数组

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

分析:

权值储存用二维数组实现,直接枚举放正方形的坐标(x,y)并计算覆盖权值即可

注意下标不要越界。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int d,n,g[130][130];
void ready(){//输入数据
    int x,y,k;
    scanf("%d%d",&d,&n);
    for(int i=1;i<=n;i++) scanf("%d%d%d",&x,&y,&k),g[x][y]=k;
}
void solve(){
    int cnt,maxv=0;
    for(int i=0;i<=128;i++)
    for(int j=0;j<=128;j++){//枚举正方形
        int L=max(0,i-d),U=max(0,j-d),
            R=min(128,i+d),D=min(128,j+d),
            t=0;//注意不要越界
        for(int i_=L;i_<=R;i_++)
          for(int j_=U;j_<=D;j_++)
            t+=g[i_][j_];
        if(t>maxv) maxv=t,cnt=1;
        else if(t==maxv) cnt++;
    }
    printf("%d %d\n",cnt,maxv);
}
int main(){
    ready();
    solve();
    return 0;
}

T2:寻找道路

考察知识:图论,图的最短路

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

分析:

我们先寻找满足条件的所有节点,然后求起点到终点的最短路就可以了。

怎么寻找满足条件的最短路呢:我们先反向见一个图,从终点开始dfs/bfs,标记可以到达的所有节点,然后枚举每一个被标记节点,枚举这个节点所有可以到达的边是否被标记,从而判断这个节点是否满足条件。

代码:

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=10005,maxm=200005;
struct edge{
    int to,next;
}e[maxm],e_[maxm];
int head[maxn],head_[maxn],np,np_,n,m,s,t;
void adde(int u,int v){
    e[++np]=(edge){v,head[u]};
    head[u]=np; 
}
void adde_(int u,int v){//反向建图 
    e_[++np_]=(edge){v,head_[u]};
    head_[u]=np_; 
}
int d[maxn];
bool node[maxn],vis[maxn];
void dfs(int i){
    vis[i]=true;
    for(int p=head_[i];p;p=e_[p].next){
        int j=e_[p].to;
        if(!vis[j]) dfs(j);
    }
}
void build(){
    int u,v;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        if(u!=v) adde(u,v),adde_(v,u);
    }
    scanf("%d%d",&s,&t);
    dfs(t);
    for(int i=1;i<=n;i++) if(vis[i]){//寻找满足条件节点 
        node[i]=true;
        for(int p=head[i];p;p=e[p].next){
            int j=e[p].to;
            if(!vis[j]){node[i]=false;break;}
        }
    }
}
void bfs(){
    queue<int>q;
    memset(vis,0,sizeof(vis));
    if(node[s]) q.push(s);
    vis[s]=true;
    while(!q.empty()){
        int i=q.front();q.pop();
        for(int p=head[i];p;p=e[p].next){
            int j=e[p].to;
            if(!vis[j]&&node[j]) vis[j]=true,d[j]=d[i]+1,q.push(j);
        }
    }
    printf("%d\n",d[t]==0?-1:d[t]);
}
int main(){
    build();
    bfs();
    return 0;
}

T3:解方程

考察知识:数论,枚举

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

分析:

高次方程求根并不简单,我们定义:f(x)=a_0+a_1x+a_2x^2+...+a_nx^n,所以我们考虑枚举[1,m]的所有整数,并判断f(x)是否等于0。

可以看到ai非常大,所以我们考虑求 f(x) mod MD,为了防止答案错误,其中MD应该取很大,而且我们应该至少取两个模数。

对于f(x)的计算,我们用秦九韶算法,总时间复杂度O(nm)。由于多次取模还是可能有巧合,但大多数情况下我们的分数可以达到90+。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int MD=10000007,MD_=100000003;
int n,m,a[105],a_[105],cnt,ansq[105];
void get_n(int& x,int& y){
    bool flag=false;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') flag=true;
        ch=getchar();
    }
    while(isdigit(ch))
        x=(x*10+ch-'0')%MD,
        y=(y*10+ch-'0')%MD_,
        ch=getchar();
    if(flag) x=-x,y=-y;
}
void solve(){
    for(int x=1;x<=m;x++) if(cnt<n){//枚举可能的根 
        int sum=a[n];
        for(int i=n;i;i--)//秦九韶算法计算 
          sum=((long long)sum*x+a[i-1])%MD;
        if(sum==0){
            sum=a_[n];
            for(int i=n;i;i--)//双重判断 
         	  sum=((long long)sum*x+a_[i-1])%MD_;
            if(sum==0) ansq[++cnt]=x;
        }
    }
    printf("%d\n",cnt);
    for(int i=1;i<=cnt;i++) printf("%d\n",ansq[i]); 
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++) get_n(a[i],a_[i]);
    solve();
    return 0;
}

猜你喜欢

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