xmu网宿科技杯 厦门大学第四届程序设计积分赛 第四场

A题:

题目链接:http://acm.xmu.edu.cn/JudgeOnline/problem.php?id=1465
 题目描述:
  将含有n个整数的数列A中的所有整数重新从小到大排序以后得到的新的数列B, 满足,
  对于任意正整数i, j(1 <= i, j <= n), 恒有Bj - Bi = j - i 。现在, 假定给你一
  个整数列C, 你需要求出将其变为连续数列所需花费的最小代价和。(将整数x变为整数
  y需要花费|x - y|的代价, 所谓代价和, 指的是改变所有数字所需花费的代价的总和)。

 解题思路:
  
  设新数列B的第一个数为x,那么对于A数列来说,x取值太大,花费的代价会变大,x取值太小
  花费也会变大,所以对于:花费=f(x)这个函数来说,这是一个先下降后上升的凹曲线,对于
  单调的曲线我们可以二分,对于凹凸曲线我们可以用三分,具体的做法是在一个(l,r)区间里取
  两个点把区间分成三等分,再求这两个点的具体数值,把对我们不利的那个点到它最近的端点
  的那段区间去掉,因为如果这个点对我们要求的值不是有利的,那么它到它最近的那个端点之间
  的那一段都不会出现更有利的(因为这个曲线是凹凸的,大家画个图就什么都明白了,否则如果
  出现更有利的,那么这个点就不是对我不利的了).
 
  这题刚才在一个群里看到了另一种解法:把B数列的Bj-Bi=j-i变换成Bj-j=bi-i,然后就是一个求这个
  数列的中位数问题了;

//共享部分
#include<math.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>

using namespace std;

long long  A[110000];

//解法一:三分
long long F(long long x,long long n){
	long long i,sum=0;
	for(i=1;i<=n;i++)
		sum+=abs(A[i]-(x+i-1));
     return sum;
}
long long find(long long n)
{
    long long mid1,mid2,l=-2000000000,r=2000000000,c=0,v;
    while(c<50){
        c++;
        mid1=(l+r)/2;
        mid2=(l+mid1)/2;
        if(F(mid1,n)<F(mid2,n))
            l=mid2;
        else
            r=mid1;
    }
    v=1;v<<=62;
    for(l=mid2-5;l<=mid1+5;l++)
        if(F(l,n)<v) v=F(l,n);
    return v;
}
int main()
{
    int n,i;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=1;i<=n;i++)
            scanf("%lld",&A[i]);
        sort(A+1,A+n+1);
        printf("%lld\n",find(n));
    }
    return 0;
}
//解法二:变换构造

int main()
{
	int n,i;
	long long ans;
	while(scanf("%d",&n)!=EOF)
	{
		for(i=1;i<=n;i++)
			scanf("%lld",&A[i]);
		sort(A+1,A+n+1);ans=0;
		for(i=1;i<=n;i++) A[i]-=i;
		sort(A+1,A+n+1);
		for(i=1;i<=n;i++)
			ans+=abs(A[i]-A[(n+1)/2]);
		printf("%lld\n",ans);
	}
	return 0;
}


B题:博弈

思路:假设先手取右上角坐标,如果后手可以赢,那么后手一定有一个必胜状态,但是不管后手的必胜的状态是什么,先手都可以在第一步取到

所以后手除了在n=1和m=1情况下可以赢外,其他情况都输;

#include<stdio.h>
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
        puts((n==1&&m==1)?"Bob":"Alice");
    return 0;
}


 

C题:

题目链接:http://acm.xmu.edu.cn/JudgeOnline/problem.php?id=1466
 题目描述:
  在一棵有向树中, 若节点u至节点v之间有一条有向边, 则我们称u为v的父节点,
  也可以称作1祖先节点。所谓节点v的k祖先节点, 指的是节点v的父节点的k-1祖先节点。
  现在, 给定你一棵以0为根节点的树, 树上的每个节点都有一个值, 你需要能快速的回
  答出从节点v至它的k祖先节点所经过的所有节点中(即v节点、v节点的1祖先、...、v节点
  的k-1祖先、v节点的k祖先), 值最大的节点的值的为多少? 其中我们默认0号节点的值为0;

 解题思路:

  我们构造一个状态pa[i][k]表示i节点的2^k级祖先,dp[i][k]表示i节点到它(2^k)-1级祖先之间
  所有祖先的最大值,那么我们更新过程就是:
  
    pa[i][k]=pa[pa[i][k-1]][k-1];  //我的2^k级祖先就是我的2^(k-1)级祖先的2^(k-1)级祖先
    dp[i][k]=Max(dp[i][k-1],dp[pa[i][k-1]][k-1]); //理解类似上面
  
  然后就是用一个dfs求出我们最多有多少级祖先,用于判断Wrong request

  最后查询一个节点v到它的k级祖先之间的所有祖先的最大值,就是把k+1进行二进制分解后
  一步步依次用dp[v][i]来取最大值了。

  总的时间复杂度为O(n*logn+q*logn*logn),其实对于后面的那个logn也是可以优化掉的;

#include<math.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>

using namespace std;
struct E
{
    int t,next;
}edge[210000];

int pa[110000][22],dp[110000][22];
int cnt[110000],head[110000],ant;

void add(int a,int b)
{
    edge[ant].t=b;
    edge[ant].next=head[a];
    head[a]=ant++;
}

void dfs(int root,int dep)
{
    int i;
    cnt[root]=dep;
    for(i=head[root];i!=-1;i=edge[i].next)
        dfs(edge[i].t,dep+1);
}

int fun(int n,int k)
{
    int m=0,i;
    while(k){
        for(i=0;i<=20;i++)
            if(k<=(1<<i)) break;
        if(k<(1<<i)) i--;
        m=max(m,dp[n][i]); k-=(1<<i);
        n=pa[n][i];
    }
    return m;
}
int main()
{
    int n,i,k,m;
    while(scanf("%d",&n)!=EOF)
    {
        ant=0;
        memset(head,-1,sizeof(head));
        for(i=1;i<=n;i++)
            scanf("%d",&dp[i][0]);
        for(i=1;i<=n;i++){
            scanf("%d",&pa[i][0]);
            add(pa[i][0],i);
        }
        for(k=1;k<=20;k++){
            for(i=1;i<=n;i++)
                pa[i][k]=pa[pa[i][k-1]][k-1];
        }
        for(k=1;k<=20;k++){
            for(i=1;i<=n;i++)
                dp[i][k]=max(dp[i][k-1],dp[pa[i][k-1]][k-1]);
        }
        dfs(0,0);
        scanf("%d",&m);
        while(m--){
            scanf("%d%d",&k,&n);
            if(k>cnt[n]){
                puts("Wrong request");
                continue;
            }
            printf("%d\n",fun(n,k+1));
        }
    }
    return 0;
}

 

 

E题:比赛的时候一直用3分来写,一直wa抓狂,求告知正确的解法。。。。。。。。。


F题:bfs+优先队列

#include<math.h>
#include<stdlib.h>
#include<stdio.h>
#include<vector>
#include<queue>
#include<string.h>
#include<algorithm>

using namespace std;
char mat[510][510];
int vis[510][510],dp[510][510],n;
int op[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
struct node
{
    int x,y;
    bool operator<(const node &tem) const{
        return dp[x][y]>dp[tem.x][tem.y];
    }
};

int bfs()
{
    node u,tmp;
    int i,j;
    priority_queue <node> que;
    for(i=1;i<=n;i++) for(j=1;j<=n;j++){ vis[i][j]=0;dp[i][j]=1000000;}
    vis[1][1]=1;dp[1][1]=0;
    u.x=u.y=1;
    que.push(u);
    while(!que.empty()){
        u=que.top();que.pop();
        if(u.x==n&&u.y==n) break;
        for(i=0;i<4;i++){
            tmp.x=u.x+op[i][0];
            tmp.y=u.y+op[i][1];
            if(tmp.x<1||tmp.x>n||tmp.y<1||tmp.y>n) continue;
            if(dp[u.x][u.y]+mat[tmp.x][tmp.y]-'0'<dp[tmp.x][tmp.y])
            {
                dp[tmp.x][tmp.y]=dp[u.x][u.y]+mat[tmp.x][tmp.y]-'0';
                if(!vis[tmp.x][tmp.y]){
                    vis[tmp.x][tmp.y]=1;
                    que.push(tmp);
                }
            }
        }
        vis[u.x][u.y]=0;
    }
    return dp[n][n];
}

int main()
{
    int i;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=1;i<=n;i++)
            scanf("%s",mat[i]+1);
        printf("%d\n",bfs());
    }
    return 0;
}


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值