您的位置:首页 > 编程语言 > C语言/C++

NOIP2009解题报告(C/C++)(潜伏者)(Hankson的趣味题)(最优贸易)(靶形数独)

2017-03-08 17:00 501 查看

2017.3.4的校内赛

这一次我们进行了NOIP2009的真题测试,算是我这几次以来最好的一次,这归功于第三题的思路较为简单和第二题的暴力能够过1/2的点。但是这不意味着这一套题很简单,与之想法,这套题稍有不慎就会有过失性失分,比如第一题容易看漏条件。下面我们来看看:

1.潜伏者



解题报告:

这道题利用筒的思路,搞一个“字典”,将一个字母的序号(ASCII码减去’a’)作为下标,将与之相对应的字母的序号存储到数组中就可以了,需要注意的是,除了不能A数组中的对应关系必须是一对一的以外,B数组也是这样。

下面来看看代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=100;
const int M=26;
char s1[N+5],s2[N+5],s3[N+5];
int len1,len2,len3;
int map[M+1];
int main()
{
freopen("spy.in","r",stdin);
freopen("spy.out","w",stdout);
memset(map,-1,sizeof(map));
scanf("%s",s1);
scanf("%s",s2);
scanf("%s",s3);
len1=strlen(s1),len2=strlen(s2),len3=strlen(s3);
for(int i=0;i<=len1-1;i++)
{
if(map[s1[i]-'A']!=-1&&map[s1[i]-'A']!=s2[i]-'A')//没有一个下标对应两个字母的情况
{
printf("Failed");
return 0;
}
map[s1[i]-'A']=s2[i]-'A';
}
for(int i=0;i<=24;i++)
for(int j=i+1;j<=25;j++)
if(map[i]==map[j])//没有两个字母对应一个下标的情况
{
printf("Failed");
return 0;
}
for(int i=0;i<=25;i++)//26个字母要集齐
if(map[i]==-1)
{
printf("Failed");
return 0;
}
for(int i=0;i<=len3-1;i++)
printf("%c",map[s3[i]-'A']+'A');
return 0;
}


2.Hankson的趣味题



解题报告:

这道题我先是想到了用暴力的方法,因为他的数据范围设这样的:



可见,我们通过最小公倍数(gcd)和最大公因数(lcm)的求解方法,可以通过枚举(枚举到b1)的方法没举出所有可能的值。



这样的暴力法不难想出,我就不细讲了。这种算法可以得50分。

那么正解是怎么样呢?我们通过观察gcd和lcm的性质,可以发现,我们只需要枚举到sqrt(b1)就可以利用其性质推出其余可能的解,而这样就不会超时。代码如下:

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=10000;
int t,a0,a1,b0,b1;
int num,tot;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
return (a/gcd(a,b))*b;
}
bool judge(int i)
{
return(i%a1!=0)?0:(gcd(i/a1,a0/a1)==1&&gcd(b1/i,b1/b0)==1);//保证满足题意
}
int main()
{
freopen("son.in","r",stdin);
freopen("son.out","w",stdout);
scanf("%d",&t);
while(t--)
{
tot=0;
scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
for(int i=1;i*i<=b1;i++)//不能用sqrt,会超时
{
if(b1%i==0)
{
if(judge(i))tot++;
if(i*i!=b1&&judge(b1/i))tot++;//推出其余可能的解
}
}
printf("%d\n",tot);
}
return 0;
}


(此外,这道题也可以用整数的唯一分解定理来做,想来也不难理解)

3.最优贸易





解题报告:

这道题是我为数不多的能够AC得图论的题,其思路非常简洁明了:就是用两次广搜(或者说是SPFA)。第一次我们从头到尾更新出最小的点权,第二次我们从尾到头更新出最大的点权。再将路过每个点时所经过的最大最小点权的两个数组一一枚举,找到最大差值就可以了。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int M=500000;
const int N=100000;
int n,m;
int head1[N+5],head2[N+5],num=0;
int w
;
int minn[N+5],maxx[N+5];
struct edge
{
int next1,next2,u,v;
edge(){next1=-1;next2=-1;   }
};
struct edge ed[2*M+5];
void build(int u,int v)
{
num++;
ed[num].u=u;
ed[num].v=v;
ed[num].next1=head1[u];
head1[u]=num;
ed[num].next2=head2[v];//为了满足从未到头找的性质,我们必须建一个反过来的图
head2[v]=num;
}
void bfs1()//从头到尾找最小
{
int front=0,rear=1;
int state
,flag
;
memset(flag,0,sizeof(flag));
memset(minn,0x7f,sizeof(minn));
state[rear]=1,minn[1]=w[1],flag[1]=1;
do
{
front++;
int u=state[front];
flag[u]=0;
for(int i=head1[u];i!=-1;i=ed[i].next1)
{
int v=ed[i].v;
if(minn[v]>minn[u]||w[v]<minn[v])
{
minn[v]=min(w[v],minn[u]);
if(flag[v]==0)state[++rear]=v;
flag[v]=1;
}
}
}while(front<rear);
}
void bfs2()//从尾到头找最大
{
int front=0,rear=1;
int state
,flag
;
memset(flag,0,sizeof(flag));
memset(maxx,-1,sizeof(maxx));
state[rear]=n,maxx
=w
,flag
=1;
do
{
front++;
int u=state[front];
for(int i=head2[u];i!=-1;i=ed[i].next2)
{
int v=ed[i].u;
if(maxx[u]>maxx[v]||w[v]>maxx[v])
{
maxx[v]=max(w[v],maxx[u]);
if(flag[v]==0)state[++rear]=v;
flag[v]=1;
}
}
flag[u]=0;
}while(front<rear);
}
int main()
{
freopen("trade.in","r",stdin);
freopen("trade.out","w",stdout);
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<=m;i++)
{
int u,v,k;
scanf("%d%d%d",&u,&v,&k);
if(k==1)build(u,v);
if(k==2)
{
build(u,v);
build(v,u);
}
}
bfs1();
bfs2();
int ans=-1;
for(int i=1;i<=n;i++)//一一枚举找最大值
ans=max(ans,maxx[i]-minn[i]);
printf("%d",ans);
return 0;
}


此外,据说这道题也可以用Tarjan+缩点的方法做出来,反正我是不会的~

4.靶形数独







解题报告:

这道题我刚开始想用DP来做,但是很快我认识到了自己的幼稚。由于这道题数据较小(宫格数较小),可以直接用搜索来解决。但只用搜索的话注定会超时,所以我们还需要位运算。下面我们看一看大神的代码:

#include <iostream>
#include <cstdlib>
#include <cstdio>

using namespace std;

const int mlen = 10;
int a[mlen][mlen],b[mlen][mlen];
int row[mlen],lie[mlen],line[mlen],ma[mlen];
int f[512],ans,node[mlen],cnt[mlen];

void init() {
for(int i = 0; i < 9; i++) b[i][0] = b[0][i] = b[8][i] = b[i][8] = 6;
for(int i = 1; i < 8; i++) b[i][1] = b[1][i] = b[7][i] = b[i][7] = 7;
for(int i = 2; i < 7; i++) b[i][2] = b[2][i] = b[6][i] = b[i][6] = 8;
for(int i = 3; i < 6; i++) b[i][3] = b[3][i] = b[5][i] = b[i][5] = 9;
b[4][4] = 10; int t = 0;
for(int i = 1, j = 0; i <= 511; i <<= 1, j++) f[i] = j;
for(int i = 0; i < 9; i++)
for(int j = 0; j < 9; j++) {
scanf("%d",&a[i][j]);
if(a[i][j] != 0){
row[i] |= 1<<j;
t = 1<<(a[i][j]-1);
if((lie[i]&t) || (line[j]&t) || (ma[i/3*3+j/3]&t)) { printf("-1\n"); exit(0); }
lie[i] |= t; line[j] |= t;
ma[i/3*3+j/3] |= t;
}else cnt[i]++;
}
}

inline void sore() {
int nowans = 0;
for(int i = 0; i < 9; i++) for(int j = 0; j < 9; j++) nowans += a[i][j]*b[i][j];
if(ans < nowans) ans = nowans;
}

void dfs(int t) {
int pos,k;
if(t == 9) { sore(); return; }
int i = node[t];
if(!cnt[i]) { dfs(t+1); return; }
cnt[i]--;
int p = (511^row[i])&(-(511^row[i]));
row[i] |= p;
int j = f[p];
pos = 511^(lie[i]|line[j]|ma[i/3*3+j/3]);
while(pos > 0) {
k = pos&(-pos); pos ^= k;
lie[i] |= k; line[j] |= k;
ma[i/3*3+j/3] |= k; a[i][j] = f[k]+1;
dfs(t);
lie[i] ^= k; line[j] ^= k;
ma[i/3*3+j/3] ^= k;
}
cnt[i]++; row[i] ^= p;
}

int main() {
freopen("sudoku.in","r",stdin);
freopen("sudoku.out","w",stdout);
init();
for(int i = 0; i < 9; i++) node[i] = i;
for(int i = 0; i < 9; i++)
for(int j = i+1; j < 9; j++)
if(cnt[node[i]] > cnt[node[j]]) node[i] ^= node[j], node[j] ^= node[i], node[i] ^= node[j];
int tot = 0;
while(!cnt[node[tot]]) tot++;
dfs(tot);
if(!ans) { printf("-1\n"); return 0; }
printf("%d\n",ans);
return 0;
}


以上

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