USACO Section 3.4 Electric Fence(数论)

Electric Fence
Don Piele

In this problem, `lattice points' in the plane are points with integer coordinates.

In order to contain his cows, Farmer John constructs a triangular electric fence by stringing a "hot" wire from the origin (0,0) to a lattice point [n,m] (0<=;n<32000, 0<m<32000), then to a lattice point on the positive x axis [p,0] (p>0), and then back to the origin (0,0).

A cow can be placed at each lattice point within the fence without touching the fence (very thin cows). Cows can not be placed on lattice points that the fence touches. How many cows can a given fence hold?

PROGRAM NAME: fence9

INPUT FORMAT

The single input line contains three space-separated integers that denote n, m, and p.

SAMPLE INPUT (file fence9.in)

7 5 10

OUTPUT FORMAT

A single line with a single integer that represents the number of cows the specified fence can hold.

SAMPLE OUTPUT (file fence9.out)

20
思路:皮克公式 

皮克定理说明了其面积S和内部格点数目a、边上格点数目b的关系:S = a + b/2 - 1。

根据三角形面积公式求出S。如果知道了b,那么三角形内部格点数目a也就求出来了。

可以证明,一条直线((0,0),(n,m))上的格点数等于n与m的最大公约数+1。

即b=gcd(n,m)+1. gcd(n,m)为n与m的最大公约数。

/*
ID:nealgav1
LANG:C++
PROG:fence9
*/
#include<fstream>
#include<cstring>
using namespace std;
ifstream cin("fence9.in");
ofstream cout("fence9.out");
int n,m,p;
int gcd(int a,int b)
{ int z;
  while(b)
  {
    z=b;b=a%b;a=z;
  }
  return z;
}
int kabs(int x)
{
  if(x<0)x=-x;
  return x;
}
int main()
{
  while(cin>>n>>m>>p)
  { if(m==0){cout<<0<<"\n";continue;}
    int sum=m*p;sum>>=1;
    int edge=gcd(n,m)+gcd(kabs(n-p),m)+p;
    sum=sum-edge/2+1;
    cout<<sum<<"\n";
  }
}



USER: Neal Gavin Gavin [nealgav1]
TASK: fence9
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.000 secs, 3356 KB]
   Test 2: TEST OK [0.000 secs, 3356 KB]
   Test 3: TEST OK [0.000 secs, 3356 KB]
   Test 4: TEST OK [0.000 secs, 3356 KB]
   Test 5: TEST OK [0.000 secs, 3356 KB]
   Test 6: TEST OK [0.000 secs, 3356 KB]
   Test 7: TEST OK [0.000 secs, 3356 KB]
   Test 8: TEST OK [0.000 secs, 3356 KB]
   Test 9: TEST OK [0.000 secs, 3356 KB]
   Test 10: TEST OK [0.000 secs, 3356 KB]
   Test 11: TEST OK [0.000 secs, 3356 KB]
   Test 12: TEST OK [0.000 secs, 3356 KB]

All tests OK.

Your program ('fence9') produced all correct answers! This is your submission #2 for this problem. Congratulations!

Here are the test data inputs:

------- test 1 ----
1 1 2
------- test 2 ----
2 2 4
------- test 3 ----
10 20 10
------- test 4 ----
0 100 100
------- test 5 ----
0 200 20000
------- test 6 ----
100 200 50
------- test 7 ----
10000 100 10
------- test 8 ----
0 20000 2
------- test 9 ----
200 30000 30000
------- test 10 ----
30000 30000 30001
------- test 11 ----
15000 100 30000
------- test 12 ----
0 31999 31999
Keep up the good work!
Thanks for your submission!

Home on the Range
Russ Cox

To count the squares, we first precompute the biggest square with lower right corner at any particular location. This is done by dynamic programming: the biggest square with lower right corner at (i, j) is the minimum of three numbers:

  • the number of consecutive uneaten grid units to the left
  • the number of consecutive uneaten grid units to the right
  • one plus the size of the biggest square with lower right corner at (i-1, j-1)

Once we've computed this information, counting squares is simple: go to each lower right corner and increment the counters for every square size between 2 and the biggest square ending at that corner.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define MAXN 250

int goodsq[MAXN][MAXN];
int bigsq[MAXN][MAXN];
int tot[MAXN+1];

int
min(int a, int b)
{
    return a < b ? a : b;
}

void
main(void)
{
    FILE *fin, *fout;
    int i, j, k, l, n, sz;

    fin = fopen("range.in", "r");
    fout = fopen("range.out", "w");
    assert(fin != NULL && fout != NULL);

    fscanf(fin, "%d\n", &n);

    for(i=0; i<n; i++) {
	for(j=0; j<n; j++)
	    goodsq[i][j] = (getc(fin) == '1');
	assert(getc(fin) == '\n');
    }

    /* calculate size of biggest square with lower right corner (i,j) */
    for(i=0; i<n; i++) {
	for(j=0; j<n; j++) {
	    for(k=i; k>=0; k--)
		if(goodsq[k][j] == 0)
		    break;

	    for(l=j; l>=0; l--)
		if(goodsq[i][l] == 0)
		    break;

	    sz = min(i-k, j-l);
	    if(i > 0 && j > 0)
		sz = min(sz, bigsq[i-1][j-1]+1);

	    bigsq[i][j] = sz;
	}
    }

    /* now just count squares */
    for(i=0; i<n; i++)
    for(j=0; j<n; j++)
    for(k=2; k<=bigsq[i][j]; k++)
	tot[k]++;

    for(i=2; i<=n; i++)
	if(tot[i])
	    fprintf(fout, "%d %d\n", i, tot[i]);
				
    exit(0);
}

Greg Price writes:

The posted solution runs in cubic time, with quadratic storage. With a little more cleverness in the dynamic programming, the task can be accomplished with only quadratic time and linear storage, and the same amount of code and coding effort. Instead of running back along the rows and columns from each square, we use the biggest-square values immediately to the west and north, so that each non-ravaged square's biggest-square value is one more than the minimum of the values to the west, north, and northwest. This saves time, bringing us from cubic to quadratic time.

Another improvement, which saves space and perhaps cleans up the code marginally, is to keep track of the number of squares of a given size as we go along. This obviates the need to keep a quadratic-size matrix of biggest-square values, because we only need the most recent row for continuing the computation. As for "ravaged" values, we only use each one once, all in order; we can just read those as we need them.

#include <fstream.h>

ifstream fin("range.in");
ofstream fout("range.out");

const unsigned short maxn = 250 + 5;

unsigned short n;
char fieldpr;
unsigned short sq[maxn]; // biggest-square values
unsigned short sqpr;
unsigned short numsq[maxn]; // number of squares of each size

unsigned short
min3(unsigned short a, unsigned short b, unsigned short c)
{
	if ((a <= b) && (a <= c))
		return a;
	else 
		return (b <= c) ? b : c;
}

void
main()
{
	unsigned short r, c;
	unsigned short i;
	unsigned short tmp;

	fin >> n;

	for (c = 1; c <= n; c++)
		sq[c] = 0;

	for (i = 2; i <= n; i++)
		numsq[i] = 0;

	for (r = 1; r <= n; r++)
	{
		sqpr = 0;
		sq[0] = 0;
		for (c = 1; c <= n; c++)
		{
			fin >> fieldpr;
			if (!(fieldpr - '0'))
			{
				sqpr = sq[c];
				sq[c] = 0;
				continue;
			}

			// Only three values needed.
			tmp = 1 + min3(sq[c-1], sqpr, sq[c]);
			sqpr = sq[c];
			sq[c] = tmp;

			// Only count maximal squares, for now.
			if (sq[c] >= 2)
				numsq[ sq[c] ]++;
		}
	}

	// Count all squares, not just maximal. 
	for (i = n-1; i >= 2; i--)
		numsq[i] += numsq[i+1];

	for (i = 2; i <= n && numsq[i]; i++)
		fout << i << ' ' << numsq[i] << endl;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值