一维/二维开关问题(poj3276/3279)

开关(翻转)问题:

一维开关问题一般n较大,无法直接模拟,在现实上建立一维依赖型数组

二维开关问题一般n比较小,模拟时先指定第一行的翻转方法,依次枚举下一行,
下一行要不要翻转直接由上一行可推出,具体实现和注意事项请看下面的代码。
(二维参考**《程序挑战与设计》**)

一维开关问题:

Face The Right Way
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 7881 Accepted: 3641
Description

Farmer John has arranged his N (1 ≤ N ≤ 5,000) cows in a row and many of them are facing forward, like good cows. Some of them are facing backward, though, and he needs them all to face forward to make his life perfect.

Fortunately, FJ recently bought an automatic cow turning machine. Since he purchased the discount model, it must be irrevocably preset to turn K (1 ≤ K ≤ N) cows at once, and it can only turn cows that are all standing next to each other in line. Each time the machine is used, it reverses the facing direction of a contiguous group of K cows in the line (one cannot use it on fewer than K cows, e.g., at the either end of the line of cows). Each cow remains in the same location as before, but ends up facing the opposite direction. A cow that starts out facing forward will be turned backward by the machine and vice-versa.

Because FJ must pick a single, never-changing value of K, please help him determine the minimum value of K that minimizes the number of operations required by the machine to make all the cows face forward. Also determine M, the minimum number of machine operations required to get all the cows facing forward using that value of K.

Input

Line 1: A single integer: N
Lines 2…N+1: Line i+1 contains a single character, F or B, indicating whether cow i is facing forward or backward.
Output

Line 1: Two space-separated integers: K and M
Sample Input

7
B
B
F
B
F
B
B
Sample Output

3 3
Hint

For K = 3, the machine must be operated three times: turn cows (1,2,3), (3,4,5), and finally (5,6,7)
Source

USACO 2007 March Gold

题意:
n头牛站成线,有朝前有朝后的的,然后每次可以选择大小为k的区间里的牛全部转向,会有一个最小操作m次使得它们全部面朝前方。问:求最小操作m,再此基础上求k。

题解:
1、5000头牛不是小数目,再怎么也得要n^2的算法,其中,枚举k是需要的,这就有n了,只能想办法给出一个n在O(n)时间内求出最小次数了。

2、对于给定的k,要想O(n)内把次数算出来,即只能扫一遍,一想到的必定是从前往后扫,遇到面朝后的就转头,但这一转牵扯太多,要改太多东西,k一大直接崩溃。[不能直接模拟,翻转,这样时间复杂度太大]

3、对于每次扫描到的第i个点,都至多只能改一次才能保证效率,即只改变化的。将牛的朝向弄成依赖型,即后者依赖于前者,这样在一个区间内[a,b]翻转时,实际上[a+1,b]的依赖关系是没有改变的,改变的只有a,b+1。[弄成依赖型,每次查询和修改都变成O(n)]

4、综上,设置一种关系表示每头牛与前一头牛的朝向,最简单的就是同向与反向的差异,不妨令同向为0,反向为1,为了使得最后都朝前,可以令一头虚拟牛(即0号牛)头朝前,然后第一头牛依赖于它。

5、因此,每次检查时,只需要更改a和a+k位置的牛的依赖关系便可以解决了,最后在检查一下剩余的牛是否全是0就结束了。

题解来源于https://www.cnblogs.com/tmeteorj/archive/2012/10/11/2720537.html

AC代码;

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int main(void)
{
	int n,k;
	char last,ch;
	char a[5005];
	int f[5005];int b[5005];
	f[0]=0;
	while(~scanf("%d",&n))
	{
		last='F';//牛0的朝向是F
		for(int i=1;i<=n;i++){
			scanf(" %c",&ch);
			if(ch==last)
			f[i]=0;
			else
			f[i]=1;
			last=ch;
		}
		int ansk,ansm=inf;
		for(k=1;k<=n;k++){
			memcpy(b,f,sizeof(f));//从f中拷贝n个字节到目标b中
			int ans=0;
			for(int i=1;i<=n-k+1;i++){///[n-k+1,n]/=k;
				if(b[i]==1)
				{
					ans++;
					b[i+k]^=1;//修改i+k位置的状态,因为长度为k的【i,i+k-1】区间被翻转了 
				}
			}
			for(int i=n-k+2;i<=n;i++)
			if(b[i]==1)//最后一个长度为k的区间f[i] 如果区间内还有补提的元素就无法满足全F 
			{
				ans=inf;
				break;
			}
			if(ansm>ans)
			{
				ansm=ans;
				ansk=k;
			}
			//for(int i=1;i<=n;i++)
			//printf("%d ",b[i]);
			//printf("\n");
		}
		printf("%d %d\n",ansk,ansm);
	}
	return 0;
}

二维开关问题:

Fliptile
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 23391 Accepted: 8322
Description

Farmer John knows that an intellectually satisfied cow is a happy cow who will give more milk. He has arranged a brainy activity for cows in which they manipulate an M × N grid (1 ≤ M ≤ 15; 1 ≤ N ≤ 15) of square tiles, each of which is colored black on one side and white on the other side.

As one would guess, when a single white tile is flipped, it changes to black; when a single black tile is flipped, it changes to white. The cows are rewarded when they flip the tiles so that each tile has the white side face up. However, the cows have rather large hooves and when they try to flip a certain tile, they also flip all the adjacent tiles (tiles that share a full edge with the flipped tile). Since the flips are tiring, the cows want to minimize the number of flips they have to make.

Help the cows determine the minimum number of flips required, and the locations to flip to achieve that minimum. If there are multiple ways to achieve the task with the minimum amount of flips, return the one with the least lexicographical ordering in the output when considered as a string. If the task is impossible, print one line with the word “IMPOSSIBLE”.

Input

Line 1: Two space-separated integers: M and N
Lines 2…M+1: Line i+1 describes the colors (left to right) of row i of the grid with N space-separated integers which are 1 for black and 0 for white
Output

Lines 1…M: Each line contains N space-separated integers, each specifying how many times to flip that particular location.
Sample Input

4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1
Sample Output

0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0
Source

USACO 2007 Open Silver

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int mi[16][16];//最优解 
int one[16][16];//记录中间 翻转 结果 (1表示翻转) 
int a[16][16];//输入 
int n,m,num/*最小翻转次数*/;
int get(int x,int y)//查询(x,y)的颜色 
{
	int next[5][2]={{-1,0},{1,0},{0,0},{0,-1},{0,+1}};//上下,自身,左右 
	int tx,ty;
	int c=a[x][y];
	for(int i=0;i<5;i++){
		tx=x+next[i][0];ty=y+next[i][1];
		if(tx>=0&&tx<m&&ty>=0&&ty<n)//合法 
		{
			c+=one[tx][ty];
		}
	}
	return c%2;
}//0表示白色,1表示黑色
int search()
{
	//第一行已经确定,所以从第二行开始枚举 
	for(int i=1;i<m;i++){
		for(int j=0;j<n;j++){//因为先确定了上一行的翻转方法,所以么
			if(get(i-1,j)==1)//如果 (i-1,j)是黑色的那么(i,j)必须翻转 
			{
				one[i][j]=1; 
			}
		}
	}
	//判断最后一行是否全白
	for(int i=0;i<m;i++)
	for(int j=0;j<n;j++)
	if(get(i,j)!=0)
	return -1;//非法解
	//统计翻转次数
	int ans=0;
	for(int i=0;i<m;i++)
	for(int j=0;j<n;j++)
	ans+=one[i][j];
	return ans;  
}
void solve()
{
	num=inf;
	//n比较小,枚举 第一行 的情况 共2^n种可能 
	for(int i=0;i<1<<n;i++){
		memset(one,0,sizeof(one));
		for(int j=0;j<n;j++)
		one[0][n-j-1]=(i>>j)&1;
		int  ans=search();//翻转次数 
		if(num>ans&&ans!=-1)
		{
			num=ans;
			memcpy(mi,one,sizeof(one));//更新最优解 
		}
	}
	if(num==inf)//无解 
	{
		printf("IMPOSSIBLE\n");
		return;
	}
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++)
		if(j!=n-1)
		printf("%d ",mi[i][j]);
		else
		printf("%d\n",mi[i][j]);
	}//输出格式有点坑人 
	return;
}
int main(void)
{
	while(~scanf("%d %d",&m,&n))
	{
		for(int i=0;i<m;i++)
		for(int j=0;j<n;j++)
		scanf("%d",&a[i][j]);//输入 
		solve();
	}
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值