noip 2010总结
机器翻译
题目背景
小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。
题目描述
这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。
假设内存中有M个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过M-1,软件会将新单词存入一个未使用的内存单元;若内存中已存入M个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。
假设一篇英语文章的长度为N个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。
输入输出格式
输入格式:
输入文件共2行。每行中两个数之间用一个空格隔开。
第一行为两个正整数M和N,代表内存容量和文章的长度。
第二行为N个非负整数,按照文章的顺序,每个数(大小不超过1000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。
输出格式:
包含一个整数,为软件需要查词典的次数。
输入输出样例
输入样例#1:
3 7
1 2 1 5 4 4 1
输出样例#1:
5
说明
每个测试点1s
对于10%的数据有M=1,N≤5。
对于100%的数据有0<=M<=100,0<=N<=1000。
整个查字典过程如下:每行表示一个单词的翻译,冒号前为本次翻译后的内存状况:空:内存初始状态为空。
1. 1:查找单词1并调入内存。
2. 1 2:查找单词2并调入内存。
3. 1 2:在内存中找到单词1。
4. 1 2 5:查找单词5并调入内存。
5. 2 5 4:查找单词4并调入内存替代单词1。
6. 2 5 4:在内存中找到单词4。
7. 5 4 1:查找单词1并调入内存替代单词2。
共计查了5次词典。
思路
模拟+队列
如果一个数在队列中,不用管
如果不在队列中,判断队列大小:
- 如果size<=m-1,将这个数加入队列
- 如果size>m,删除队首元素,将这个数压入队尾
用数组
模拟队列
即可
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, ans = 0, sizee = 0, b[10010];
int main()
{
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i++)
{
bool flag = false;
int a;
scanf("%d", &a);
for (int j = 1; j <= sizee; j++)
if (b[j] == a){
flag = true;
break;
}
if (flag == 0){
ans++;
if (sizee < m){
sizee++;
b[sizee] = a;
}
if (sizee == m){
for (int j = 2; j <= m; j++)
b[j - 1] = b[j];
b[m] = a;
}
}
}
printf("%d", ans);
return 0;
}
乌龟棋
题目背景
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
题目描述
乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型的卡片,见样例),每种类型的卡片上分别标有1、2、3、4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
输入输出格式
输入格式:
输入文件的每行中两个数之间用一个空格隔开。
第1行2个正整数N和M,分别表示棋盘格子数和爬行卡片数。
第2行N个非负整数,a1a2……aN,其中ai表示棋盘第i个格子上的分数。
第3行M个整数,b1b2……bM,表示M张爬行卡片上的数字。
输入数据保证到达终点时刚好用光M张爬行卡片。
输出格式:
输出只有1行,1个整数,表示小明最多能得到的分数。
输入输出样例
输入样例#1:
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
输出样例#1:
73
说明
每个测试点1s
小明使用爬行卡片顺序为1,1,3,1,2,得到的分数为6+10+14+8+18+17=73。注意,由于起点是1,所以自动获得第1格的分数6。
数据范围
对于30%的数据有1≤N≤30,1≤M≤12。
对于50%的数据有1≤N≤120,1≤M≤50,且4种爬行卡片,每种卡片的张数不会超过20。
对于100%的数据有1≤N≤350,1≤M≤120,且4种爬行卡片,每种卡片的张数不会超过40;0≤ai≤100,1≤i≤N;1≤bi≤4,1≤i≤M。
思路
- 这种分步决策的问题先考虑动态规划。
- 由于有4种不同的卡片,所以我们肯定要在状态上把这个反映出来,要不然显然转移不了状态。
- 还有一点就是我们知道了四种卡片用了的数量我们自然就可以知道走了多少步,这样一来问题就很简单了。
- f[x1][x2][x3][x4]表示四种卡片分别用了x1,x2,x3,x4张时的最大得分
- 容易得到f[x1][x2][x3][x4]=max(f[x1-1][x2][x3][x4],f[x1][x2-1][x3][x4],f[x1][x2][x3-1][x4],f[x1][x2][x3][x4-1])+a[x1+x22+x33+x4*4]
代码
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
inline int max(int a, int b) {
if (a > b) return a;
else return b;
}
int f[42][42][42][42], i, n, m, num[5], a[400], k, l1, l2, l3, l4;
int main()
{
scanf("%d%d", &n, &m);
for (i = 1; i <= n; i++)
scanf("%d", &a[i]);
memset(num, 0, sizeof(num));
for (i = 1; i <= m; i++)
{
scanf("%d", &k);
num[k]++;
}
memset(f, 0, sizeof(0));
int t;
f[0][0][0][0] = a[1];
for (l1 = 0; l1 <= num[1]; l1++)
for (l2 = 0; l2 <= num[2]; l2++)
for (l3 = 0; l3 <= num[3]; l3++)
for (l4 = 0; l4 <= num[4]; l4++)
{
t = l1 + l2 * 2 + l3 * 3 + l4 * 4 + 1;
if (l1)f[l1][l2][l3][l4] = max(f[l1][l2][l3][l4], f[l1 - 1][l2][l3][l4] + a[t]);
if (l2)f[l1][l2][l3][l4] = max(f[l1][l2][l3][l4], f[l1][l2 - 1][l3][l4] + a[t]);
if (l3)f[l1][l2][l3][l4] = max(f[l1][l2][l3][l4], f[l1][l2][l3 - 1][l4] + a[t]);
if (l4)f[l1][l2][l3][l4] = max(f[l1][l2][l3][l4], f[l1][l2][l3][l4 - 1] + a[t]);
}
printf("%d", f[num[1]][num[2]][num[3]][num[4]]);
}
关押罪犯
题目描述
S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为c 的冲突事件。
每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到S 城Z 市长那里。公务繁忙的Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。
在详细考察了N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。
那么,应如何分配罪犯,才能使Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?
输入输出格式
输入格式:
输入文件的每行中两个数之间用一个空格隔开。第一行为两个正整数N 和M,分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的M 行每行为三个正整数aj,bj,cj,表示aj 号和bj 号罪犯之间存在仇恨,其怨气值为cj。数据保证1<aj=<=bj<=N ,0 < cj≤ 1,000,000,000,且每对罪犯组合只出现一次。
输出格式:
共1 行,为Z 市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出0。
输入输出样例
输入样例#1:
4 61 4 25342 3 35121 2 283511 3 66182 4 18053 4 12884
输出样例#1:
3512
说明
【输入输出样例说明】罪犯之间的怨气值如下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件影响力是3512(由2 号和3 号罪犯引发)。其他任何分法都不会比这个分法更优。
【数据范围】对于30%的数据有N≤ 15。对于70%的数据有N≤ 2000,M≤ 50000。对于100%的数据有N≤ 20000,M≤ 100000。
代码
#include <algorithm>
#include <iostream>
using namespace std;
int n,m,f[40001],x,y;
struct data
{
int a,b,c;
}e[100001];
int gz(const data &a,const data &b){
if(a.c>b.c)return 1;
else return 0;
}
int find(int x) {
return f[x]==x?x:f[x]=find(f[x]);
}
int main() {
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>e[i].a>>e[i].b>>e[i].c;
for(int i=1;i<=n*2;i++)
f[i]=i;
sort(e+1,e+m+1,gz);
for(int i=1;i<=m;i++)
{
x=find(e[i].a);
y=find(e[i].b);
if(x==y)
{
cout<<e[i].c;
return 0;
}
f[y] = find(e[i].a + n);
f[x] = find(e[i].b + n);
}
cout<<0;
return 0;
}
引水入城
题目描述
在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政 区划十分特殊,刚好构成一个N行M列的矩形,如上图所示,其中每个格子都代表一座城 市,每座城市都有一个海拔高度。 为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施 有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的 蓄水池中。因此,只有与湖泊毗邻的第1行的城市可以建造蓄水厂。而输水站的功能则是通 过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是 存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。 由于第N行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利 设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干 旱区中不可能建有水利设施的城市数目。
输入输出
输入描述 Input Description
输入的每行中两个数之间用一个空格隔开。 输入的第一行是两个正整数N和M,表示矩形的规模。 接下来N行,每行M个正整数,依次代表每座城市的海拔高度。
输出描述 Output Description
输出有两行。如果能满足要求,输出的第一行是整数1,第二行是一个整数,代表最少 建造几个蓄水厂;如果不能满足要求,输出的第一行是整数0,第二行是一个整数,代表有 几座干旱区中的城市不可能建有水利设施。
样例
样例输入 Sample Input
2 5
9 1 5 4 3
8 7 6 1 2
样例输出 Sample Output
1
1
数据范围及提示 Data Size & Hint
数据范围
本题共有10个测试数据,每个数据的范围如下表所示: 测试数据编号 能否满足要求 N M 1 不能 ≤ 10 ≤ 10 2 不能 ≤ 100 ≤ 100 3 不能 ≤ 500 ≤ 500 4 能 = 1 ≤ 10 5 能 ≤ 10 ≤ 10 6 能 ≤ 100 ≤ 20 7 能 ≤ 100 ≤ 50 8 能 ≤ 100 ≤ 100 9 能 ≤ 200 ≤ 200 10 能 ≤ 500 ≤ 500 对于所有的10个数据,每座城市的海拔高度都不超过10^6
样例2 说明
数据范围
题解
显然每个第一行的点要覆盖最后一行连续的一段区间,如果有些点不能覆盖,则这些点一定不能满足
从上往下灌水得出每个最后一行的点是否能被覆盖,回答第一问
从下往上反灌水得出每个第一行的点覆盖区间的左右端点
区间排序做线段覆盖
复杂度 O(nm+mlogm)
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define mod 1000000007
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int xx[4]={1,-1,0,0},yy[4]={0,0,1,-1};
int n,m;
int a[505][505],mp[505][505],l[505][505],r[505][505];
int qx[250005],qy[250005];
struct data{int l,r;}c[505];
bool operator<(data a,data b)
{
return a.l==b.l?a.r<b.r:a.l<b.l;
}
void bfs(int b[505][505],int x,int y,int v,bool f)
{
int head=0,tail=1;
qx[0]=x;qy[0]=y;b[x][y]=v;
while(head!=tail)
{
int x=qx[head],y=qy[head];head++;
for(int k=0;k<4;k++)
{
int nowx=x+xx[k],nowy=y+yy[k];
if(nowx<1||nowy<1||nowx>n||nowy>m||b[nowx][nowy])continue;
if(f==0&&a[nowx][nowy]>=a[x][y])continue;
if(f==1&&a[nowx][nowy]<=a[x][y])continue;
b[nowx][nowy]=b[x][y];
qx[tail]=nowx;qy[tail]=nowy;tail++;
}
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=read();
for(int i=1;i<=m;i++)
bfs(mp,1,i,1,0);
int tot=0;
for(int i=1;i<=m;i++)
if(!mp[n][i])tot++;
if(tot)
{
printf("0\n%d\n",tot);
return 0;
}
for(int i=1;i<=m;i++)if(!l[n][i])bfs(l,n,i,i,1);
for(int i=m;i;i--)if(!r[n][i])bfs(r,n,i,i,1);
for(int i=1;i<=m;i++)
{
if(!l[1][i])l[1][i]=r[1][i];
if(!r[1][i])r[1][i]=l[1][i];
c[i].l=l[1][i];c[i].r=r[1][i];
}
int now=0,to=0;
sort(c+1,c+m+1);
for(int i=1;i<=m;i++)
{
if(now+1>=c[i].l)to=max(to,c[i].r);
else now=to,to=max(to,c[i].r),tot++;
}
if(now!=m)tot++;
printf("1\n%d\n",tot);
return 0;
}