第一题 叶子合并
【题目描述】
在一个美丽的秋天,丽丽每天都经过的花园小巷落满了树叶,她决定把树叶堆成K堆,小巷是笔直的,共有N片树叶(树叶排列也是笔直的),每片树叶都有一个重量值,并且每两片想邻的树叶之间的距离都是1,现把所有的树叶按从左到右的顺序进行编号,编号为1..N。丽丽移动每片树叶所消耗能量等于这片树叶的重量乘以移动的距离,丽丽决定分K天完成,每天堆一堆,并且规定只能把树叶往左移动,因为丽丽每天都是从右往左经过小巷的。 求丽丽完成任务所消耗的最少能量。
【输入格式】
输入的第一行为两个用空格隔开的正整数N和K。后面有N行,每行一个正整数表示叶子的重量(第i+1行表示第i片树叶的重量)。
【输出格式】
输出为一个整数,表示把树叶堆成K堆所消耗的最少体力。
【样例输入】
5 2
1
2
3
4
5
【样例输出】
13
【数据范围】
0<N<1001
0<K<11,K<N
0<每片树叶的重量<1001
【分析】
预处理sn[i] = a[1] * 0 + a[2] * 1 + … + a[i] * (i – 1)。
s[i][j]是a[i] + … + a[j]。
f[i][j] = min{f[k + 1][j – 1] + sn[k] – sn[i – 1] – s[i][j] * (i – 1)}; (i <= k <= n)
初始化为极大值,f[n + 1][0] = 0。
第二题 游戏
【题目描述】
一个N*N的矩阵。NYY和XYY做游戏。给出一个数M。如果M是奇数,NYY逐行取数字,XYY逐列取数字。如果M是偶数,XYY逐行取数字,NYY逐列取数字。
举例:
8 0 7
2 5 6
1 3 4
如果M是5,那么NYY取得的数字是8 0 7 2 5 6 1 3 4,XYY取得的数字是8 2 1 0 5 3 7 6 4。
然后对两个人的数列求逆序对的个数。个数大的获胜。
【输入格式】
包含多个测试数据。第一行为N和M。接下来的N行,每行N个数,表示N*N的矩阵。
【输出格式】
如果NYY获胜,输出“NYY wins the Rocket's Game”。
如果XYY获胜,输出“XYY wins the Rocket's Game”。
如果是平局,输出“NYY and XYY will try again”。
输出的两行之间必须有一个空行。
【样例输入】
1 3
0
3 3
1 0 3
4 2 5
7 8 6
4 3
1 2 5 12
4 6 9 15
11 8 10 14
3 7 0 13
【样例输出】
NYY and XYY will try again
NYY wins the Rocket's Game
XYY wins the Rocket's Game
【数据范围】
1 <= n <= 200;
1 <= m <= 6.
【分析】
先构建出两个数列,然后求逆序对的个数。用到了归并排序的思想。
对于数列a[l]…a[r],设mid = (l + r) / 2。首先递归处理a[l]…a[mid]和a[mid + 1]…a[r]。然后两端数组都是升序的了。假设左面处理到p1,右面处理到p2。如果a[p1] > a[p2],逆序对的个数就加上左半部分剩下的元素的个数,他们都是比a[p2]大的。
第三题 简单的连通块
【题目描述】
在一个连通块里,如果任意一个顶点最多只属于一个环,那么我们称该连通块是“简单的”。 给你一个无向图(可能含有多个连通块),要你求该无向图有多少个连通块是“简单的”。
【输入格式】
第一行N 和 M ,其中N表示无向图的顶点数, M表示无向图的边数。1 ≤ N ≤ 300 , 0 ≤ M ≤ 30000.
接下来有M行, 每行两个整数a, b. 表示a 到 b之间有一条边。
【输出格式】
输出无向图中有多少个连通块是“简单的”
【样例输入】
3 3
1 2
1 3
2 3
【样例输出】
1
【分析】
h[i]代表每个点连出的所有边在环中的个数。对每个点进行深搜。dfn[x]是x是第几个被搜索到的。low[x]代表x的深搜子树中能追溯到的最早的节点。对于x的下一个点y来说,如果y已经被搜索到,那么low[x] = min(low[x],low[y])。如果没有被搜索到,搜索y,如果low[y]>dfn[x],那么显然y和x不是一个环中的点。将两个点的h值都减一。
如果low[y] < low[x],更新low[x]。
然后分别统计每个联通块中的点,找到连通块中最大的h值,如果大于2,显然存在两个或者以上的环。
第四题 有线电视网
【题目描述】
某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。 从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号的费用总和。 现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪些用户提供信号而不给哪些用户提供信号。
写一个程序找出一个方案使得有线电视网在不亏本的情况下使观看转播的用户尽可能多。
【输入格式】
输入文件的第一行包含两个用空格隔开的整数N和M,其中2 ≤ N ≤ 3000,1 ≤ M ≤ N-1,N为整个有线电视网的结点总数,M为用户终端的数量。
第一个转播站即树的根结点编号为1,其他的转播站编号为2到N - M,用户终端编号为N – M + 1到N。
接下来的N - M行每行表示—个转播站的数据,第i + 1行表示第i个转播站的数据,其格式如下:
K A1 C1 A2 C2 … Ak Ck
K表示该转播站下接K个结点(转播站或用户),每个结点对应一对整数A与C,A表示结点编号,C表示从当前转播站传输信号到结点A的费用。最后一行依次表示所有用户为观看比赛而准备支付的钱数。
【输出格式】
输出文件仅一行,包含一个整数,表示上述问题所要求的最大用户数。
【样例输入】
5 3
2 2 2 5 3
2 3 2 4 3
3 4 2
【样例输出】
2
【分析】
f[i][j]代表以i为根的子树取j个子节点,最大的盈利(可能为负)。a[x]代表x节点的盈利。lnum[x]代表x的子树中叶子节点的个数。链表son[x]存x的子节点。
动归过程中,先计算x点的lnum值。然后枚举子节点。f[x][j] = max(f[y][k] + f[x][j - k] - z)。z是当前儿子节点y的花费。0 < j <= lnum[x],0 <= k <= min(lnum[y],j)。如果x是叶子节点,f[x][1] = a[x],lnum[x] = 1。
初始值为负的极大值。
代码
第一题
#include <stdio.h>
#define MAXN 1010
#define MAXINT 100000010
int f[MAXN][11],a[MAXN],s[MAXN][MAXN],sn[MAXN];
int n,m;
int main() {
freopen("leaves.in","r",stdin);
freopen("leaves.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i = 1;i <= n;++i)
scanf("%d",&a[i]);
for (int i = 1;i <= n;++i)
sn[i] = sn[i - 1] + a[i] * (i - 1);
for (int i = 1;i <= n;++i)
for (int j = i;j <= n;++j)
s[i][j] = s[i][j - 1] + a[j];
for (int i = 1;i <= n + 1;++i)
for (int j = 0;j <= m;++j)
f[i][j] = MAXINT;
f[n + 1][0] = 0;
for (int i = n;i > 0;--i)
for (int j = 1;j <= m;++j)
for (int k = i;k <= n;++k)
if (f[k + 1][j - 1] < MAXINT)
if (f[k + 1][j - 1] + sn[k] - sn[i - 1] - s[i][k] * (i - 1) < f[i][j])
f[i][j] = f[k + 1][j - 1] + sn[k] - sn[i - 1] - s[i][k] * (i - 1);
printf("%d\n",f[1][m]);
return 0;
}
第二题
#include <stdio.h>
#define MAXN 210
#define MAXINT 10000010
int te[MAXN * MAXN],a[MAXN][MAXN];
int tot,t1,t2,n,m;
void dodo(int l,int r) {
if (l >= r)
return;
int mid = (l + r) >> 1;
dodo(l,mid);
dodo(mid + 1,r);
int te1[mid - l + 10],te2[r - mid + 10];
for (int i = l;i <= mid;++i)
te1[i - l + 1] = te[i];
te1[mid - l + 2] = MAXINT;
for (int i = mid + 1;i <= r;++i)
te2[i - mid] = te[i];
te2[r - mid + 1] = MAXINT;
int p1 = 1,p2 = 1;
for (int i = l;i <= r;++i) {
if (te1[p1] < te2[p2])
te[i] = te1[p1++];
else {
te[i] = te2[p2++];
tot += mid - p1 + 1;
}
}
}
int solve() {
for (int i = 1;i <= n;++i)
for (int j = 1;j <= n;++j)
scanf("%d",&a[i][j]);
int len = 0;
for (int i = 1;i <= n;++i)
for (int j = 1;j <= n;++j)
te[++len] = a[i][j];
tot = 0;
dodo(1,len);
if (m & 1)
t1 = tot;
else
t2 = tot;
len = 0;
for (int j = 1;j <= n;++j)
for (int i = 1;i <= n;++i)
te[++len] = a[i][j];
tot = 0;
dodo(1,len);
if (m & 1)
t2 = tot;
else
t1 = tot;
if (t1 < t2)
printf("NYY wins the Rocket's Game\n");
else
if (t2 < t1)
printf("XYY wins the Rocket's Game\n");
else
printf("NYY and XYY will try again\n");
printf("\n");
}
int main() {
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
while (scanf("%d%d",&n,&m) != EOF)
solve();
return 0;
}
第三题
#include <stdio.h>
#include <string.h>
#define MAXN 300
bool have[MAXN][MAXN];
struct tnode {
int num;
tnode *next;
} g[MAXN],*t;
int h[MAXN],v[MAXN],low[MAXN],dfn[MAXN];
int time,n,m,x,y,maxh,ans;
void ins(int x,tnode &p) {
t = new(tnode);
t->num = x;
t->next = p.next;
p.next = t;
}
void find(int x,int last) {
v[x] = 1;
low[x] = dfn[x] = ++time;
tnode *te = g[x].next;
int y;
while (te != NULL) {
y = te->num;
if (y != last)
if (!v[y]) {
find(y,x);
if (low[y] > dfn[x]) {
--h[y];
--h[x];
}
if (low[y] < low[x])
low[x] = low[y];
} else {
if (low[y] < low[x])
low[x] = low[y];
}
te = te->next;
}
}
void find_max(int x) {
if (h[x] > maxh)
maxh = h[x];
v[x] = 1;
tnode *te = g[x].next;
int y;
while (te != NULL) {
y = te->num;
if (!v[y])
find_max(y);
te = te->next;
}
}
int main() {
freopen("simple.in","r",stdin);
freopen("simple.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i = 1;i <= m;++i) {
scanf("%d%d",&x,&y);
if (!have[x][y]) {
have[x][y] = have[y][x] = 1;
ins(x,g[y]);
ins(y,g[x]);
++h[x];
++h[y];
}
}
for (int i = 1;i <= n;++i)
if (!v[i])
find(i,0);
memset(v,0,sizeof(v));
for (int i = 1;i <= n;++i)
if (!v[i]) {
maxh = 0;
find_max(i);
if (maxh <= 2)
++ans;
}
printf("%d\n",ans);
return 0;
}
第四题
#include <stdio.h>
#include <iostream>
using namespace std;
#define MAXN 3010
#define MAXINT 1000010
int a[MAXN],f[MAXN][MAXN],lnum[MAXN];
struct tn {
int num,cost;
tn *next;
} son[MAXN],*t;
int ans,n,m;
void ins(int x,int y,tn &p) {
t = new(tn);
t->num = x;
t->cost = y;
t->next = p.next;
p.next = t;
}
int find(int x) {
if (x > n - m) {
f[x][1] = a[x];
lnum[x] = 1;
} else {
int y,z;
tn *te = son[x].next;
while (te != NULL) {
y = te->num;
z = te->cost;
find(y);
lnum[x] += lnum[y];
te = te->next;
}
te = son[x].next;
while (te != NULL) {
y = te->num;
z = te->cost;
for (int j = lnum[x];j > 0;--j)
for (int k = min(lnum[y],j); k >= 0;--k)
f[x][j] = max(f[x][j],f[y][k] + f[x][j - k] - z);
te = te->next;
}
}
}
int main() {
freopen("tele.in","r",stdin);
freopen("tele.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i = 1;i <= n;++i)
for (int j = 1;j <= m;++j)
f[i][j] = -MAXINT;
for (int i = 1;i <= n - m;++i) {
int si;
scanf("%d",&si);
for (int j = 1;j <= si;++j) {
int x,y;
scanf("%d%d",&x,&y);
ins(x,y,son[i]);
}
}
for (int i = n - m + 1;i <= n;++i)
scanf("%d",&a[i]);
find(1);
ans = 0;
for (int i = 1;i <= m;++i)
if ((f[1][i] >= 0) && (i > ans))
ans = i;
printf("%d\n",ans);
return 0;
}