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;
}