1. 程式人生 > >[jzoj]2018.07.15【NOIP普及組】模擬賽D組:解題報告

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