您的位置:首页 > 其它

NENU ACM 13级训练赛 2014-12-06(福州大学第十一届程序设计竞赛)

2014-12-06 17:17 260 查看
福大的校赛,题目是中文的,省去了大家读题的时间。

A题:水题

每7天就有一个周六和周日,算一算就行了。

<span style="font-size:18px;">#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;

int main(){
int n;
while(~scanf("%d",&n)){
int num = n/7;
int left = n%7;
int ans = num*2;
if(left == 6) ans++;
printf("%d\n",ans);
}
return 0;
}</span>


B题:规律

类似单调队列,其实就是个O(n)的递推水题,把样例推一推就清楚了

2 1 3 1 4

2*1 + 1*2 + 3*3 (1)

1*1 + 3*2 + 1*3 (2)

3*1 + 1*2 + 4*3 (3)

式子(1)为 13

式子(2)为 13 - 2*1 - (1+3) + 1*3 = 10

即: 13 - (2+1+3) + 1*3 = 10

式子(3)为 10 - 1*1 - (3+1) + 4*3 = 17

即: 10 - (1+3+1) + 4*3 = 17

可以发现,式子(i)式子(i-1)减去sum(x[i-m]~x[i])再加上新的项。

这样扫一遍取最大就可以了

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;

const int MAXN = 1000010;
int a[MAXN];
int sum[MAXN];
int n,m;

int main() {
while(~scanf("%d%d",&n,&m)) {
sum[0] = 0;
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
sum[i] = sum[i-1] + a[i];
}
int ans = -1;
int tmp = 0;
for(int i=1; i<=m; i++) {
tmp += i*a[i];
}
ans = max(ans,tmp);
for(int i=m+1; i<=n; i++) {
tmp = tmp - (sum[i-1]-sum[i-m-1]) + m*a[i];
ans = max(tmp,ans);
}
printf("%d\n",ans);
}
return 0;
}</span>


C题:DFS

由题可知,给出的图是一颗树。

设1为根,我们都知道对于树,若一条边只能走一次,则从根到各个节点的路径是唯一的,而且是最短的。

用邻接表建图,从根开始深搜进去,遍历每一个节点,在回溯的过程中如果是军队要走的路径,就把节点标记一下。

最后把这些被标记的结点上的敌人求个和,就是答案。

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>

#define mt(a,b) memset(a,b,sizeof(a))
using namespace std;

const int M = 100100;

bool vis[M];
int val[M];

struct G{
struct E{
int v,next;
}e[M<<1];
int le,head[M];
void init(){
le=0;
mt(head,-1);
}
void add(int u,int v){
e[le].v=v;
e[le].next=head[u];
head[u]=le++;
}
}g;

int fa[M];
void dfs(int u){
for(int i=g.head[u];~i;i=g.e[i].next){
int v = g.e[i].v;
if(fa[u]==v) continue;
fa[v] = u;
dfs(v);
if(vis[v]){
vis[u]=true;
}
}
}

int main(){
int n,k;
while(~scanf("%d%d",&n,&k)){

mt(vis,false);
mt(fa,-1);

g.init();
for(int i=1;i<=n;i++){
scanf("%d",&val[i]);
}
int tmp;
for(int i=1;i<=k;i++){
scanf("%d",&tmp);
vis[tmp] = true;
}
int x,y;
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
g.add(x,y);
g.add(y,x);
}

dfs(1);

int sum = 0;
for(int i=1;i<=n;i++){
if(vis[i]){
sum += val[i];
}
}
printf("%d\n",sum);
}
return 0;
}</span>


D:DP

动态规划的重点就是定义出状态,已经状态的变化,据此写出状态转移方程之后,问题就迎刃而解了。

定义dp[i][j]表示到第i位时,有j位属于第一个串。

那么对于第i位的放法,就有放在第一个串和不放在第一个串这两种选择,然后更新数组,推到下一个状态。

注意推的过程中要模mod

为了节省空间,我采用了滚动数组,大概不用滚动数组也是能AC的,可以试试。

对于这个题目转移思想和滚动数组的使用,建议认真研究一下《背包九讲》

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>

#define LL __int64
#define mt(a,b) memset(a,b,sizeof(a))

using namespace std;

const LL MOD = 1e9+7;
const int M = 3010;

char str[M];
LL dp[2][M];

int n;

int main() {
int _;
scanf("%d",&_);
while(_--) {
scanf("%d",&n);
scanf("%s",str);
mt(dp,0);
dp[0][0] = 1;
int now = 1;
for(int i=0; i<2*n; i++) {
mt(dp[now&1],0);
if (str[i]=='B') {
for (int j=0; j<=n; j++) {
//奇数
if (j&1)  {
dp[now&1][j+1] = (dp[now&1][j+1] + dp[i&1][j]) % MOD;

}
if ((i-j)&1) {
dp[now&1][j] = (dp[now&1][j] + dp[i&1][j]) % MOD;
}
}
} else {
for (int j=0; j<=n; j++) {
if ((j&1) == 0) {
dp[now&1][j+1] = (dp[now&1][j+1] + dp[i&1][j]) % MOD;
}
if (((i-j)&1) == 0) {
dp[now&1][j] = (dp[now&1][j] + dp[i&1][j]) % MOD;
}
}
}
now++;
}
printf("%I64d\n",dp[0]
);
}
return 0;
}


E:线段树

很明显的线段树的题,采用lazy数组延迟标记,区间查询区间更新。

属于基础题,望多加巩固练习。

题目说有30%的小数据,可能暴力能过吧,我不清楚,我看见有人裸暴TLE,还是稳一点吧。

#include<cstdio>
#define lrrt int L,int R,int rt
#define iall 1,n,1
#define imid int mid=(L+R)>>1
#define lson L,mid,rt<<1
#define rson mid+1,R,rt<<1|1
const int M=1e5+10;
int a[M];
struct T {
int sum,lazy;
} tree[M<<2];
void pushup(int rt) {
tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
}
void build(lrrt) {
tree[rt].lazy=0;
if(L==R) {
tree[rt].sum=a[L];
return ;
}
imid;
build(lson);
build(rson);
pushup(rt);
}
void pushdown(int mid,lrrt) {
if(tree[rt].lazy) {
tree[rt<<1].lazy+=tree[rt].lazy;
tree[rt<<1|1].lazy+=tree[rt].lazy;
tree[rt<<1].sum+=(mid-L+1)*tree[rt].lazy;
tree[rt<<1|1].sum+=(R-mid)*tree[rt].lazy;
tree[rt].lazy=0;
}
}
int query(int x,int y,lrrt) {
if(x<=L&&R<=y) return tree[rt].sum;
imid;
pushdown(mid,L,R,rt);
int ans=0;
if(mid>=x) ans+=query(x,y,lson);
if(mid<y)  ans+=query(x,y,rson);
return ans;
}
void update(int x,int y,int z,lrrt) {
if(x<=L&&R<=y) {
tree[rt].sum+=(R-L+1)*z;
tree[rt].lazy+=z;
return ;
}
imid;
pushdown(mid,L,R,rt);
if(mid>=x) update(x,y,z,lson);
if(mid<y)  update(x,y,z,rson);
pushup(rt);
}
int main() {
int n,m,q,x;
while(~scanf("%d%d%d",&n,&m,&q)) {
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
}
build(iall);
while(q--) {
scanf("%d",&x);
printf("%d\n",query(x,x+m-1,iall));
update(x,x+m-1,-1,iall);
}
}
return 0;
}


F题:待更新

G题:多源最短路+矩阵快速幂

题目要求从1点到n点的恰好k次最短路

可以很快想到floyd求出各个点之间的最短路,但是k很大,不能递推过去。

可以先求出1秒之后,各个点之间的最短路,得出一个矩阵,然后我们可以用这个矩阵继续推下去,推k-1次,这样就可以想到用矩阵快速幂了。

其实在这里,矩阵快速幂起到了加速的效果。

注意本题的矩阵快速幂并不是常规的矩阵快速幂,需要改写一下,具体看代码。

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>

#define LL __int64
#define mt(a,b) memset(a,b,sizeof(a))

using namespace std;

const LL inf = 0x3f3f3f3f3f3f3f3fLL;

class Matrix {
typedef LL typev;
static const int MV=55;
friend Matrix operator *(const Matrix &a,const Matrix &b) {
Matrix ret;
ret.n=a.n;
for(int i=0; i<a.n; i++) {
for(int j=0; j<b.n; j++) {
LL tmp = inf;
for(int k=0; k<a.n; k++) {
tmp = min(tmp, a.val[i][k] + b.val[k][j]);
}
ret.val[i][j]=tmp;
}
}
return ret;
}
friend Matrix operator ^ (Matrix a,LL b) {
Matrix ret = a;
while(b) {
if(b&1) ret = a*ret;
a = a*a;
b>>=1;
}
return ret;
}
public:
int n,m;///n行m列
typev val[MV][MV];
void init(int tn) {
mt(val,inf);
n = tn;
}
} A;

int main() {
int n,h;
LL k;
int _;
scanf("%d",&_);
while(_--) {
scanf("%d%d%I64d",&n,&h,&k);
A.init(n);

int u,v;
LL w;
while(h--) {
scanf("%d%d%I64d",&u,&v,&w);
u--;
v--;
A.val[u][v] = min(A.val[u][v],w);
}

Matrix ans = A^(k-1);

if(ans.val[0][n-1] == inf) puts("-1");
else printf("%I64d\n",ans.val[0][n-1]);

}
return 0;
}
</span>


H题:待更新
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: