T1 中位数(median.cpp)
【题目描述】
有一个长度为N的数列{A1,A2,…An},这N和数字恰好是1..N的一个排列。你需要统计有多少个子序列{Ai,Ai+1,…Aj}满足:i<=j且j-i+1为奇数,序列的中位数为B。例如{5,1,3}的中位数为3。
【输入文件】
第一行包含两个正整数N和B;第二行包含N个整数,第i个整数为Ai。
【输出文件】
仅包含一个整数,为满足条件的子序列的个数。
【样例输入】
7 4
5 7 2 4 3 1 6
【样例输出】
4
【数据规模】
对于30%的数据中,满足N<=100;
对于60%的数据中,满足N<=1000;
对于100%的数据中,满足N<=100000,1<=B<=N。
【题解】【规律+递推】
【因为B是序列的中位数,所以B一定存在于序列中,所以,记录B在序列中的位置num,并把相对应的数置为0;把小于B的数置为-1;大于B的数置为1。】
【分别从num向左向右扫描,并分别设一个累加器,将枚举时遇到的0、1、-1都加入累加器里,并分别用L、R数组记录累加器中每个数出现的次数】
【最后求:∑L[i]*R[-i](但因为C++没有负数下标,所以要整体向右移n个下标)】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[100010],L[200010],R[200010],n,b,num,ans;
int main()
{
freopen("median.in","r",stdin);
freopen("median.out","w",stdout);
int i,j;
scanf("%d%d",&n,&b);
for(i=1;i<=n;++i)
{
scanf("%d",&a[i]);
if(a[i]==b) {num=i; a[i]=0; continue;}
if(a[i]>b) a[i]=1;
else a[i]=-1;
}
int l=n,r=n;
for(i=num;i>0;--i) l+=a[i],L[l]++;
for(i=num;i<=n;++i) r+=a[i],R[r]++;
int len=2*n;
for(i=0;i<=len;++i)
ans+=(L[i]*R[len-i]);
printf("%d\n",ans);
return 0;
}
T2 敲砖块
【题目描述】
在一个凹槽中放置了N层砖块,最上面的一层油N块砖,从上到下每层一次减少一块砖。每块砖都有一个分支,敲掉这块砖就能得到相应的分值,如图所示。
如果你想敲掉第i层的第j块砖的话,若i=1,你可以直接敲掉它;若i>1,则你必须先敲掉第i-1层的第j和第j+1块砖。
你现在可以敲掉最多M块砖,求得分最多能有多少。
【输入文件】
输入文件的第一行有两个正整数N和M;
接下来的N行,描述这N层砖块上的分值A[i,j],满足0<=A[i,j]<=100。
【输出文件】
仅一行,包含一个整数,为最大的得分。
【样例输入】
4 5
2 2 3 4
8 2 7
2 3
49
【样例输出】
19
【数据规模】
对于20%的数据,满足1<=N<=10,1<=M<=30
对于100%的数据,满足1<=N<=50,1<=M<=500。
首先将砖块全部左对齐,变成一个直角三角形的模样。
可以得出如下结论:
1、每一列必须敲到由上到下的若干砖块。
2、如果某一列敲掉了k个砖块,那么其右边的那一列至少敲掉了k-1个砖块。
设f[i][j][k]表示从右到左已经敲到了第i列,其中第i列敲掉了j个砖块且总共敲掉了k个砖块的最大得分.
那么转移方程为f[i][j][k]=max f[i+1][v][k−j]+a[1][i]+a[2][i]+……+a[j][i] (v>=j−1)。
最终答案为max{ f[i][j][k] }。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,a[510][510],fi,fj,fk,v[510][510],f[60][60][510],ans;
int main()
{
freopen("brike.in","r",stdin);
freopen("brike.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=n-i+1;j++)
{
scanf("%d",&a[i][j]);
}
for (int i=n;i>=1;i--)
for (int j=1;j<=n-i+1;j++)
v[i][j]=v[i][j-1]+a[j][i];
memset(f,-1,sizeof(f));
f[n+1][0][m]=0;
for (int j=n;j>=1;j--)
for (int i=0;i<=n-j+1;i++)
for (int k=m-i;k>=0;k--)
{
for (int l=i-1;l<=n-j+1;l++)
if (f[j+1][l][k+i]!=-1)
f[j][i][k]=max(f[j][i][k],f[j+1][l][k+i]+v[j][i]);
if (ans<f[j][i][k])
{
ans=f[j][i][k];
}
}
printf("%d",ans);
}
T3 邮递员送信
【问题描述】
有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间。这个邮递员每次只能带一样东西。求送完这N-1样东西并且最终回到邮局最少需要多少时间。
【样例输入】
5 10
2 3 5
1 5 5
3 5 6
1 2 8
1 3 8
5 3 4
4 1 8
4 5 3
3 5 6
5 4 2
【样例输出】
83
【题解 思路1】
题解:大水题,考试想到了两遍最短路,却没有继续向深层思考。只要将边反过来,那之前是1到2~n个点的最短路,反过来就是2~n个点到1的最短路,两遍dijkstra便可。同时要注意题目中有重边出现,用邻接矩阵注意判重,这也显示出了邻接表的优势,邻接表不需要判重。
#include<cmath>
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,maxx=1e9,minn,t,z,i,j,k,x,y;
long long ans;
bool flag[1006];
int c[1006],d[1006];
int f1[1006][1006],f2[1006][1006];
int get()
{
int x=0,p=1;
char c;
c=getchar();
while (c<'0'||c>'9') {if (c=='-') p=-1;c=getchar();}
while (c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
return x*p;
}
int main()
{
n=get();m=get();
memset(f1,127/3,sizeof(f1));
memset(f2,127/3,sizeof(f2));
memset(c,127/3,sizeof(c));
memset(d,127/3,sizeof(d));
maxx=f1[0][0];
for (i=1;i<=m;i++)
{
x=get();y=get();z=get();
if (f1[x][y]!=maxx) f1[x][y]=min(f1[x][y],z);
else f1[x][y]=z;
if (f2[y][x]!=maxx) f2[y][x]=min(f2[y][x],z);
else f2[y][x]=z;
}
memset(flag,false,sizeof(flag));
for (i=1;i<=n;i++) c[i]=f1[1][i];
flag[1]=true;
c[1]=0;
for (i=1;i<=n-1;i++)
{
minn=maxx;
k=0;
for (j=1;j<=n;j++)
if ((!flag[j])&&(c[j]<minn))
{
minn=c[j];
k=j;
}
if (k==0) break;
flag[k]=true;
for (j=1;j<=n;j++)
{
if (c[k]+f1[k][j]<c[j])
c[j]=c[k]+f1[k][j];
}
}
memset(flag,false,sizeof(flag));
for (i=1;i<=n;i++) d[i]=f2[1][i];
flag[1]=true;
d[1]=0;
for (i=1;i<=n-1;i++)
{
minn=maxx;
k=0;
for (j=1;j<=n;j++)
if ((!flag[j])&&(d[j]<minn))
{
minn=d[j];
k=j;
}
if (k==0) break;
flag[k]=true;
for (j=1;j<=n;j++)
{
if (d[k]+f2[k][j]<d[j])
d[j]=d[k]+f2[k][j];
}
}
for (i=1;i<=n;i++)
ans+=c[i]+d[i];
printf("%lld",ans);
}
【题解】【最短路】
【这道题思路十分精妙,因为是有向图,所以刚开始按数据建图,跑一遍SPFA;再把所有边反向,再跑一遍SPFA。把两次的最短路都加入答案】
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int f[1010][1010];
int a[100010],nxt[100010],p[1010],val[100010],tot;
int n,m;
ll ans,dis[1010];
inline void add(int x,int y,int v)
{
tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot; val[tot]=v;
}
inline void spfa()
{
queue<int>que;
memset(dis,127/3,sizeof(dis));
dis[1]=0; que.push(1);
while(!que.empty())
{
int u=que.front(); que.pop();
int v=p[u];
while(v!=-1)
{
if(dis[a[v]]>dis[u]+(ll)val[v])
{
dis[a[v]]=dis[u]+(ll)val[v];
que.push(a[v]);
}
v=nxt[v];
}
}
for(int i=2;i<=n;++i) ans+=dis[i];
return;
}
int main()
{
freopen("post.in","r",stdin);
freopen("post.out","w",stdout);
int i,j;
memset(f,127,sizeof(f));
memset(p,-1,sizeof(p));
memset(nxt,-1,sizeof(nxt));
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(f[x][y]>z) f[x][y]=z;
add(x,y,z);
}
spfa(); tot=0;
memset(p,-1,sizeof(p));
memset(nxt,-1,sizeof(nxt));
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
if(i!=j&&f[i][j]!=f[0][0])
add(j,i,f[i][j]);
spfa();
printf("%lld\n",ans);
return 0;
}