[jzoj]2018.07.15【NOIP普及组】模拟赛D组:解题报告

目录:

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]);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_40155097/article/details/81085840