Description
FGD小朋友特别喜欢爬山,在爬山的时候他就在研究山峰和山谷。为了能够让他对他的旅程有一个安排,他想
知道山峰和山谷的数量。给定一个地图,为FGD想要旅行的区域,地图被分为n*n的网格,每个格子(i,j) 的高度w(
i,j)是给定的。若两个格子有公共顶点,那么他们就是相邻的格子。(所以与(i,j)相邻的格子有(i?1, j?1),(i?1
,j),(i?1,j+1),(i,j?1),(i,j+1),(i+1,j?1),(i+1,j),(i+1,j+1))。我们定义一个格子的集合S为山峰(山谷)当
且仅当:1.S的所有格子都有相同的高度。2.S的所有格子都联通3.对于s属于S,与s相邻的s’不属于S。都有ws >
ws’(山峰),或者ws < ws’(山谷)。你的任务是,对于给定的地图,求出山峰和山谷的数量,如果所有格子
都有相同的高度,那么整个地图即是山峰,又是山谷。
Input
第一行包含一个正整数n,表示地图的大小(1<=n<=1000)。接下来一个n*n的矩阵,表示地图上每个格子的高
度。(0<=w<=1000000000)
Output
应包含两个数,分别表示山峰和山谷的数量。
Sample Input
5
8 8 8 7 7
7 7 8 8 7
7 7 7 7 7
7 8 8 7 8
7 8 8 8 8
输入样例2
5
5 7 8 3 1
5 5 7 6 6
6 6 6 2 8
5 7 2 5 8
7 1 0 1 7
Sample Output
2 1
输出样例2
3 3
考试代码:
#include<iostream>
#include<cstdio>
using namespace std;
long long w[1001][1001];
bool a[1001][1001];
int dx[8]={1,0,-1,0,-1,-1,1,1},dy[8]={0,1,0,-1,1,-1,-1,1};
int top,dip;int n,ans;
bool t,d;
struct node{
int x,y;
}q[1001*1001];
void bfs(int x,int y,int h)
{
int head=1,tail=0,tx,ty,count=0;
a[x][y]=1;
q[head].x=x;
q[head].y=y;
do
{
count++;
tail++;
tx=q[tail].x;
ty=q[tail].y;
for (int i=0;i<4;i++)
{
if(tx+dx[i]>=1&&tx+dx[i]<=n&&ty+dy[i]>=1&&ty+dy[i]<=n&&a[tx+dx[i]][ty+dy[i]]==h)
{x=tx+dx[i];
y=ty+dy[i];}//应该标为走过即加上a[x][y]=1;
if (1==a[x][y])continue;
a[x][y]=1;
head++;
q[head].x=x;
q[head].y=y;
if(x>=1&&y>=1&&x<=n-1&&y<=n-1)
for(int j=0;j<8;j++)//应该不限定高度一样而让他到处走,不用单独去走四周
{
if(w[x+dx[j]][y+dy[j]]>h)d=1;
if(w[x+dx[j]][y+dy[j]]<h)t=1;
if(d&&t)break;//用标记的技术低级
}
}
}
while(head!=tail);
return;
}
int main()
{
//freopen("grz.in","r",stdin);freopen("grz.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)scanf("%lld",&w[i][j]);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(!a[i][j])
{
//cout<<a[i][j]<<endl;
bfs(i,j,w[i][j]);
if(d&&t){d=0,t=0;}
else
if(d){dip++;d=0;}
else {top++;t=0; }
// ans++;
}
}
}
//cout<<"ans"<<ans<<endl;
printf("%d %d",top,dip);
}
正解答案:
#include<iostream>
#include<cstdlib>
#include<cstdio>
#define now a[quex[tot]][quey[tot]]
using namespace std;
int a[1005][1005];
int n,ans[3],flag,f,nx,ny;
int fx[8]={0,0,1,-1,1,1,-1,-1};
int fy[8]={1,-1,0,0,-1,1,-1,1};
int quex[2000005],quey[2000005],tot,tow;
bool vis[1005][1005];
char ch;
inline void orzcy(int &s){
s=0;ch=getchar();f=1;
for (;ch<'0'||ch>'9';ch=getchar())
if (ch=='-')f=-1;
for (;ch>='0'&&ch<='9';ch=getchar())
s=(s<<3)+(s<<1)+ch-'0';
}//读入优化(可以借鉴);
inline void bfs(int x,int y){
quex[tot=1]=x;quey[tow=1]=y;vis[x][y]=1;
while (tot<=tow)
{
for (int i=0;i<8;++i)
{
nx=quex[tot]+fx[i];
ny=quey[tot]+fy[i];
if (nx<1 || nx>n || ny<1 || ny>n)continue;//如果出界跳过
if (a[nx][ny]==now && !vis[nx][ny])//如果没走过且高度相同
{
quex[++tow]=nx;
quey[tow]=ny;
vis[nx][ny]=1;
}
else//这个标记法高级,1为山峰,2为山谷;
if (a[nx][ny]<now)//出现一次比四周比它低的就可能不是山峰了
{
if (flag!=1&&flag!=-1)flag=0;//同下
else flag=1;//标记为山峰
}
else if (a[nx][ny]>now)
{
if (flag!=2 && flag!=-1)flag=0;//如果之前没标记为山谷,现在又出现四周比它高,说明不是山峰也不是山谷
else flag=2;//如果之前标记为了山谷,或者是第一次,标记为山谷
}
}
++tot;
}
}
int main (){
orzcy(n);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
orzcy(a[i][j]);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
if (!vis[i][j])
{
flag=-1;//初始是-1
bfs(i,j);
if (flag!=-1)ans[flag]++;//如果不是初始状态,即分别把山峰山谷分别++
else {ans[1]++;ans[2]++;}//如果全部高度相同既是山峰也是山谷
}
printf ("%d %d",ans[1],ans[2]);
return 0;
}
Description
在一个无限的棋盘上有一个超级马,它可以完成各种动作。每一种动作都是通过两个整数来确定——第一个数说明列的数(正数向右,负数向左),第二个数说明行的数(正数向上,负数向下),移动马来完成这个动作。
编写一个程序,从文本文件SUP.IN输入说明各种超级马的数据库。
对每一个超级马进行确认,是否通过自己的行动可以到达盘面上的每一个区。
将结果存储到文本文件SUP.OUT。
Input Format
在文本文件SUP.IN 的第一行中存在一个整数k,它代表数据库的数1≤k≤100。在这个数字后出现K 数据库。它们的每一个第一行中会出现整数N,它是马能够完成的各种动作的数,1≤n≤100。接下来数据库的每一个行中包含两个整数P 和Q,它们由单个空格分开,说明动作,-100≤p,q≤100。
OutputFormat
文本文件SUP.OUT 应由K 行组成,当第i 个数据库的超级马可以到达棋盘面的每一个区,第i 行应包含一个词TAK,而另一个词NIE 则恰恰相反。
TimeLimit 1s
MemoryLimit 256M
Sample Input
2
3
1 0
0 1
-2 -1
5
3 4
-3 -6
2 -2
5 6
-1 4
Sample Output
TAK
NIE
--------------------------------------------------------------------------------------------
分析:
此题棋盘无限大,所以要想让马通过走遍棋盘来判断是不可能的,我们换个思路想,若它能到达棋盘上的每一个点,则必须可以从一个点走到相邻的四个点,于是,问题就变成了如何让马从(0,0)走到(1,0)(0,1)(-1,0)(0,-1)这四个点,因为每移动一次,横纵坐标最大变化为100,所以我们可以把搜索范围缩小到(-100..100,-100..100),即在这个范围内,若能从(0,0)走到(1,0)(0,1)(-1,0)(0,-1)这四个点,则它就能走遍整个棋盘,经过转换的这一题用BFS比较方便,另外,一开始我们可以预处理一下,若每一种走法横坐标的最大公约数或每一种走法纵坐标的最大公约数大于1,则直接输出NIE。以上是一种思路;
可以减枝,但是不减也能过了,算啦懒得打了;
答案:
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
using namespace std;
int a[205][205];
int n,k,ans[3],f,nx,ny;
int fx[101],fy[101];
int quex[2000005],quey[2000005],tot,tow;
bool vis[205][205];
bool bfs()
{
quex[tot=1]=100;quey[tow=1]=100;vis[100][100]=1;
while (tot<=tow)
{
for (int i=1;i<=n;++i)
{
nx=quex[tot]+fx[i];
ny=quey[tot]+fy[i];
if (nx>=0 && nx<=200 && ny>=0 && ny<=200)
if (!vis[nx][ny])
{
quex[++tow]=nx;
quey[tow]=ny;
vis[nx][ny]=1;
if(vis[100][99] && vis[100][101] && vis[99][100] && vis[101][100])
return true;
}
}
++tot;
}
if(vis[100][99] && vis[100][101] && vis[99][100] && vis[101][100])return true;
else return false;
}
int main ()
{
orzcy(k);
for(int i=1;i<=k;i++)
{
memset(fx,0,sizeof(fx));memset(fy,0,sizeof(fy));memset(vis,0,sizeof(vis));
orzcy(n);
for(int j=1;j<=n;j++)
{
scanf("%d%d",&fx[j],&fy[j]);
}
if(bfs())printf("TAK\n");
else printf("NIE\n");
/*for(int o=99;o<=101;o++)
{
for(int p=99;p<=101;p++)
cout<<vis[o][p]<<" ";
cout<<endl;
}*/
}
return 0;
}
传递游戏(pass) Description
n个人在做传递物品的游戏,编号为1-n。
游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位;下一个人可以传递给未接过物品的任意一人。
即物品只能经过同一个人一次,而且每次传递过程都有一个代价;不同的人传给不同的人的代价值之间没有联系;
求当物品经过所有n个人后,整个过程的最小总代价是多少。
Input Format
第一行为n,表示共有n个人(16>=n>=2);
以下为n*n的矩阵,第i+1行、第j列表示物品从编号为i的人传递到编号为j的人所花费的代价,特别的有第i+1行、第i列为-1(因为物品不能自己传给自己),其他数据均为正整数(<=10000)。
Output Format
一个数,为最小的代价总和。
Sample Input
2
-1 9794
2724 –1
Sample Output
2724
Solution
dfs:
传3个参数,一个表示当前传到的人,一个是传过了多少人,一个是当前代价;
最优化剪枝:若当前代价加上(所有人中最小的代价*(n-传过的人数))>=ans则剪掉。可以1s出解,
自己的代码:
#include<cstdio>
#include<iostream>
using namespace std;
int a[20][20];
bool yes[20];
int n,ans,p,mi=422542255,bin;
void dfs(int m,int count,int sum)
{
ans=656454454;
if(count==n)
{
if(sum<ans)
ans=sum;
// cout<<ans<<endl;
return ;
}
else {
for(int i=1;i<=n;i++)
{
if(a[m][i]<mi)mi=a[m][i];
if(i!=m&&!yes[i])
{
yes[i]=1;
dfs(i,count+1,sum+a[m][i]);
yes[i]=0;
}
if(mi*(n-count)>=ans)return;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++){
memset(yes,0,sizeof(yes));
dfs(i,1,0);}
cout<<ans<<endl;
}
别人的代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<climits>
using namespace std;
int n, map[17][17], ans = INT_MAX;
int times, maxm;
bool vis[17];
void dfs(int k, int lft, int now) {
if(lft == n) {
ans = min(ans, now);
return;
}
if(now+(n-lft)*maxm >= ans) return;
for(int i = n; i >= 1; --i) if(!vis[i]) {
vis[i] = true;
dfs(i, lft+1, now + map[k][i]);
vis[i] = false;
}
}
int main() {
freopen("pass.in", "r", stdin);
freopen("pass.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j){
scanf("%d", &map[i][j]);
if(i != j) maxm = min(maxm, map[i][j]);
}
for(int i = n; i >= 1; --i) {
memset(vis, false, sizeof(vis));
vis[i] = true;
dfs(i, 1, 0);
}
printf("%d", ans);
return 0;
}
进一步优化{
dp:
方程:f[i][k] = min(f[i][k], f[j][k^pos[i-1]]+map[j][i]);
i,j表示由j传到了i,k是一个二进制数表示状态,1为当前传过了某位置,0则为未传过,详见代码。
code:
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cstring>
using namespace std;
int pos[17] = {1, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 1<<16};
//二进制记录状态,1表示该位置的人已经传过,0表示没有传过
int n, map[17][17];
int f[17][70000], ans = 707406378;
inline void read(int &a) {//读入优化
int f = 1;
char s = getchar();
while(s<'0' || s>'9') {
if(s == '-') f = -1;
s = getchar();
}
while(s>='0' && s<='9') {
a = a*10+s-48;
s = getchar();
}
a *= f;
}
inline void write(int a) {//输出优化
char s[20];
int top = 0;
if(a < 0) {
putchar('-');
a = -a;
}
do {
s[++top] = a%10+48;
a /= 10;
} while(a);
for(int i = top; i >= 1; --i) putchar(s[i]);
}
inline void init() {//读入数据
//freopen("pass.in", "r", stdin);
read(n);
for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j)
read(map[i][j]);
}
inline void print() {//输出数据
// freopen("pass.out", "w", stdout);
write(ans);
}
inline void work() {//dp
memset(f, 127/3, sizeof(f));
for(int i = 1; i <= n; ++i) f[i][pos[i-1]] = 0;
for(int k = 0; k <= pos[n]-1; ++k) //枚举已经传过了哪些人
for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) {//当前由j传到了i
if(i == j) continue;
if((k&pos[i-1]) != pos[i-1]) continue;//若不等,则说明没有传过i,不符
if((k&pos[j-1]) != pos[j-1]) continue;//若不等,则说明没有传过j,不符
f[i][k] = min(f[i][k], f[j][k^pos[i-1]]+map[j][i]);//k^pos[i-1]表示的状态就是pos[i-1]并把i的位置去掉
}
for(int i = 1; i <= n; ++i) ans = min(f[i][pos[n]-1], ans);
}
int main() {
init();//输入
work();
print();
return 0;
}
模块化编程,还没来得及仔细研究;5月21日;