目录:
1.马农
2.马语翻译
3.马球比赛
4.棋盘游戏
1.马农
题目描述:
在观看完战马检阅之后,来自大草原的两兄弟决心成为超级“马农”,专门饲养战马。
兄弟两回到草原,将可以养马的区域,分为 N*N 的单位面积的正方形, 并实地进行考察,归纳出了每个单位面积可以养马所获得的收益。接下来就要开始规划他们各自的马场了。
首先,两人的马场都必须是矩形区域。同时,为了方便两人互相照应,也为了防止马匹互相走散,规定两个马场的矩形区域相邻,且只有一个交点。最后,互不认输的两人希望两个马场的收益相当,这样才不会影响他们兄弟的感情。
现在,兄弟两找到你这位设计师,希望你给他们设计马场,问共有多少种设计方案。
输入:
第一行一个整数 N,表示整个草原的大小为 N*N。
接下来 N 行,每行 N 个整数 A(i,j),表示第 i 行第 j 列的单位草地的收成。
(注意:收益可能是负数,养马也不是包赚的,马匹也可能出现生病死亡等意外。)
输出:
输出符合两人要求的草原分配方案数。
样例输入:
3
1 2 3
4 5 6
7 8 9
样例输出:
2
数据范围限制:
40%的数据, N<=10。
100%的数据, N<=50, -1000<A(i,j)<1000。
思路:
穷举每个矩形的交点,每个交点会把整个区域划分成四块,如下图
(20170702221706705.png)
①区域对应④区域,③区域对应②区域。分别穷举每个区域里的矩形,注意矩形的一个顶点一定为交点。然后用哈希记录收益值,找出对应区域相等收益的个数。还有一个优化,在代码注释里面有介绍。
Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#define LL long long
#define M 2500000
using namespace std;
int n,sum[55][55];
int ans,x,tmp,top;
int h[M*2+10],st[55*55];
int main()
{
while(~scanf("%d",&n))
{
memset(sum,0,sizeof(sum));
ans=top=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&x);
sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+x;
}
}
for(int i=1;i<n;i++)
{
for(int j=1;j<n;j++) //这两个for穷举了交点
{
//cout<<sum[i][j]<<' ';
for(int k=1;k<=i;k++)
{
for(int l=1;l<=j;l++) //这两个是穷举区域里面的矩形
{
tmp=sum[i][j]+sum[k-1][l-1]-sum[k-1][j]-sum[i][l-1]+M;
st[top++]=tmp; //用哈希记录 ,用map超时orz
h[tmp]++;
}
}
for(int k=i+1;k<=n;k++)
{
for(int l=j+1;l<=n;l++)
{
tmp=sum[i][j]+sum[k][l]-sum[k][j]-sum[i][l]+M;
ans+=h[tmp];
}
}
while(top) //这里用了一个堆栈记录哈希的个数,直接清空对应的
h[st[--top]]=0; //如果直接清空整个数组,应该是会超时的
for(int k=i+1;k<=n;k++)
{
for(int l=1;l<=j;l++)
{
tmp=sum[i][l-1]+sum[k][j]-sum[i][j]-sum[k][l-1]+M;
st[top++]=tmp;
h[tmp]++;
}
}
for(int k=1;k<=i;k++)
{
for(int l=j+1;l<=n;l++)
{
tmp=sum[i][l]+sum[k-1][j]-sum[k-1][l]-sum[i][j]+M;
ans+=h[tmp];
}
}
while(top)
h[st[--top]]=0;
}
//cout<<endl;
}
printf("%d\n",ans);
}
}
2.马语翻译
题目描述:
随着马场的繁荣,出现了越来越多的新马种。种族之间的沟通不畅严重影响了马场的和谐。这时,科学家发明了马语翻译机器人,正好可以解决这一难题。
机器人有 M 种,每种机器人能完成 K 个马种之间的语言翻译。问,利用这些机器人,能否实现 1 种群和 N 种群的马语翻译。 若可以,找到翻译过程至少需要用到多少种语言。
输入:
第一行三个整数 N, K 和 M,分别表示语言数, 每个机器人能翻译的语言数, 机器人的数量。
接下来 M 行,每行 K 个整数。表示每个机器人可以翻译的语言编号(编号从 1 到 N)。
输出:
输出最少转换语言的次数。如果无法完成翻译,输出-1。
数据范围限制:
40%的数据 N<=100, 1<=K<=20, M<=40。
100%的数据 1<=N<=100000, 1<=K<=1000, 1<=M<=1000。
思路:
这是一道图论题目,要求出从语言1到语言n的最短路,因为N比较大,显然用bfs(或spfa)效率更高,但是关键在于怎么建图。
对每个机器人建图?
每两个机器人如果有共同的语言就连一条边。
枚举两个机器人,枚举机器人的语言。
时间复杂度O(KM^2),显然超时。
对每种语言建图?
对每一个机器人的所以语言两两建图。
枚举一个机器人,枚举机器人的两种语言。
时间复杂度O(MK^2),显然超时。
换个思路,对机器人和语言同时建图:把机器人变成点,与语言连边,通往机器人的边权为1,其余为零。
那么最终的结果就是从语言1到语言n所经过的节点数/(div) 2 + 1
Code:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<cstdio>
#define MAXN 101005
#define MAXE 2000005
using namespace std;
int n,k,m,cnt,q[MAXN],dis[MAXN],lnk[MAXN],son[MAXE],nxt[MAXE];
bool vis[MAXN];
inline int read()
{
int ret = 0,w = 0;
char ch = 0;
while(!isdigit(ch))
{
w |= ch == '-';
ch = getchar();
}
while(isdigit(ch))
{
ret = (ret << 3) + (ret << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -ret : ret;
}
inline void write(int ret)
{
if(ret < 0)
{
putchar('-');
ret = -ret;
}
if(ret > 9)
write(ret / 10);
putchar(ret % 10 + '0');
}
void add(int x,int y)
{
son[++cnt] = y,nxt[cnt] = lnk[x],lnk[x] = cnt;
}
void bfs()
{
int head = 0,tail = 1;
q[1] = 1;
vis[1] = 1;
while (head ^ tail)
{
int x = q[++head];
for (int j = lnk[x];j;j = nxt[j])
if (!vis[son[j]])
{
vis[son[j]] = 1;
dis[son[j]] = dis[x] + 1;
q[++tail] = son[j];
}
}
}
int main()
{
//freopen("trans.in","r",stdin);
//freopen("trans.out","w",stdout);
n = read();
k = read();
m = read();
for (int i = 1;i <= m;i++)
for (int j = 1;j <= k;j++)
{
int x = read();
add(x,i + n);
add(i + n,x);
}
bfs();
if (vis[n])
printf("%d",(dis[n] >> 1) + 1);
else
cout << "-1";
return 0;
}
3.马球比赛
题目:
在解决了马语翻译问题后,马匹数量越来越多,不少乡镇都有了数量可观的马匹,开始出现马球比赛。乡镇之间决定进行马球联赛。
联赛的赛制,主要是比赛双方的马匹数量,成了一个急需解决的问题。首先,所有乡镇都要求,本乡镇所有的马匹都必须参赛,或者都不参赛(若组队的马匹数量不是该镇马匹数量的约数,将无法参赛)。其次,在本乡镇,选出最佳球队,参加乡镇间联赛。
现在,比赛组织方希望满足所有参赛乡镇的要求,并且使得决赛的马匹尽可能多,请你设计每个球队马匹的数量,使得决赛马匹数最大。注意,决赛至少有 2 个队伍晋级。
输入:
第一行一个整数 N,表示想要报名参赛的乡镇。
接下来 N 个用空格分开的整数 a(i),表示第 i 个乡镇报名参赛的马匹数。
输出:
计算出决赛最大参与的马匹数。
数据范围:
20%的数据 2<=N<=100, 1<=a(i)<=10000。
50%的数据 2<=N<=20000。
100%的数据 2<=N<=200000, 1<=a(i)<= 2000000。
思路:
卡常 + 伪DP
Code:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<cmath>
using namespace std;
inline int read()
{
int ret = 0,w = 0;
char ch = 0;
while(!isdigit(ch))
{
w |= ch == '-';
ch = getchar();
}
while(isdigit(ch))
{
ret = (ret << 3) + (ret << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -ret : ret;
}
inline void write(int ret)
{
if(ret < 0)
{
putchar('-');
ret = -ret;
}
if(ret > 9)
write(ret / 10);
putchar(ret % 10 + '0');
}
long long f[2000010],p[2000010];
int n,x,y,z,ls;
int main()
{
long long s;
n = read();
for(int i = 1;i <= n;i++)
{
x = read();
f[x]++;
if(x > z)
z = x;
}
s = n;
for(int i = 2;i <= z;i++)
if(f[i] > 0)
{
for(int j = 2;j * j <= i;j++)
{
if(p[i] >= 2)
continue;
p[j]++;
if(i % j == 0)
{
f[j] += f[i];
if(j * j != i)
f[i / j] += f[i];
}
}
}
for(int i = 1;i <= z;i++)
{
if (f[i] > 1 && f[i] * i > s)
s = f[i] * i;
}
write(s);
}
4.棋盘游戏
题目描述:
给定一个N*M的棋盘,每个格子里最多只可以放置一个棋子,求有多少种放置方案使得任意2*2的正方形区域内恰有2个棋子。
输入:
棋盘的长与宽 N M
输出:
一个整数,代表可行的方案数。
数据范围:
对于30%的数据 N,M<=20
对于100%的数据 N,M<=10000
思路:
打了表之后发现了规律:2 ^ n + 2 ^ m - 2;
但一看数据范围就知道直接套公式会炸,所以要用高精度
Code:
# include<cctype>
# include<cstdio>
# include<cstring>
using namespace std;
int n,m;
int sum[5010],t[5010],min_[5010],k;
inline int read()
{
int ret = 0,w = 0;
char ch = 0;
while(!isdigit(ch))
{
w |= ch == '-';
ch = getchar();
}
while(isdigit(ch))
{
ret = (ret << 3) + (ret << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -ret : ret;
}
inline void write(int ret)
{
if(ret < 0)
{
putchar('-');
ret = -ret;
}
if(ret > 9)
write(ret / 10);
putchar(ret % 10 + '0');
}
int main()
{
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
n = read();
m = read();
min_[1] = 1;
sum[1] = 1;
for(register int i = 1;i <= n;++i)
for(register int j = 1;j <= 5000;++j)
{
sum[j] = sum[j] * 2 + k;
k = sum[j] / 10;
sum[j] %= 10;
}
for(register int i = 1;i <= 5000;++i)
{
sum[i] -= min_[i];
if(sum[i] < 0)
sum[i] += 10,--sum[i + 1];
}
t[1] = 1;
for(register int i = 1;i <= m;++i)
for(register int j = 1;j <= 5000;++j)
{
t[j] = t[j] * 2 + k;
k = t[j] / 10;
t[j] %= 10;
}
for(register int i = 1;i <= 5000;++i)
{
t[i] -= min_[i];
if(t[i] < 0)
{
t[i] += 10;
--t[i + 1];
}
}
k = 0;
for(register int i = 1;i <= 5000;++i)
{
sum[i] = sum[i] + t[i] + k;
k = sum[i] / 10;
sum[i] %= 10;
}
bool p;
for(register int i = 5000;i >= 1;--i)
{
p = sum[i] ? 1 : p;
if(p)
write(sum[i]);
}
}