A.费解的开关
题意:
给1个5*5
的灯矩阵 0
表示灯暗 1
表示灯亮
若改变其中一个灯的状态 其四周的灯的状态也会变化
问能否在6步之内将所有灯打开。
题解:
跟我之前写的poj3279题目很相似 poj3279
多用1个cnt记录一下步数 大于6时剪枝即可
代码:
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1e5+7;
const int inf = 1e9+7;
typedef long long LL;
int T;
char s[10];
int m[10][10],num[10][10],cnt;
int ans;
bool check(int x,int y)
{
int temp=m[x][y];
temp+=num[x-1][y]+num[x][y]+num[x][y-1]+num[x][y+1];
return temp%2;
}
int main()
{
scanf("%d",&T);
while(T--)
{
ans=inf;
for(int i=1;i<=5;i++)
{
scanf("%s",s+1);
for(int j=1;j<=5;j++)
m[i][j]=s[j]-'0';
}
for(int i=0;i<(1<<5);i++)
{
memset(num,0,sizeof(num));
cnt=0;
for(int j=0;j<5;j++)
{
num[1][5-j]=(i>>j)&1;
cnt+=(i>>j)&1;
}
for(int j=2;j<=5 && cnt<=6;j++)
{
for(int k=1;k<=5 && cnt<=6;k++)
{
if(!check(j-1,k))
{
cnt++;
num[j][k]=1;
}
}
}
if(cnt<=6)
{
int ok=1;
for(int j=1;j<=5;j++)
if(!check(5,j))
{
ok=0;
break;
}
if(ok)
ans=min(ans,cnt);
}
}
if(ans==inf)
printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}
B.Ultra-QuickSort
题意:
给一个长度为N
的数组a[]
,问a[]
中有多少对逆序对?
题解:
逆序对:递减的2个数组成的数对(A[i]>A[j] && i
1.冒泡排序。冒泡排序的过程其实就是发现有逆序对就交换2个数的位置,所以只要在排序过程中计数即可。但由于这道题的数据是5e5,O(n^2)会超时。
2.线段树(树状数组)。O(nlogn)。
3.归并排序。O(nlogn)。
2和3用的原理都是一样的
假设数组里的数字前k个是非递减的,第k+1个比前k个中的x个小,那么添加第k+1个数字时逆序对就增加了x个
我用的是归并排序,具体就不讲了,以后有时间再看看吧。。。数据爆int要用LL
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 5e5+7;
const int inf = 1e9+7;
typedef long long LL;
int n;
int a[maxn],temp[maxn],cnt;
LL ans;
void Merge(int l,int m,int r)
{
int p=l,q=m+1;
cnt=0;
while(p<=m && q<=r)
{
if(a[p]<=a[q])
temp[cnt++]=a[p++];
else
{
ans+=m+1-p;
temp[cnt++]=a[q++];
}
}
while(p<=m) temp[cnt++]=a[p++];
while(q<=r) temp[cnt++]=a[q++];
for(int i=0;i<cnt;i++)
a[i+l]=temp[i];
}
void Sort(int l,int r)
{
if(l==r)
return;
int m=(l+r)/2;
Sort(l,m);
Sort(m+1,r);
Merge(l,m,r);
}
int main()
{
while(scanf("%d",&n)!=EOF && n)
{
ans=0;
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
Sort(0,n-1);
//for(int i=0;i
//printf("%d ",a[i]);
printf("%lld\n",ans);
}
return 0;
}
C.货仓选址
题意:
有N个工厂,给出N个工厂的横坐标X[i],要建立一个货仓给N个工厂配送东西,问建在哪里可以使货仓到各个工厂的路程之和最小?
题解:
贪心,显然看到这道题就把坐标按从小到大排序,然后发现建在中位数时路程之和最小。
代码:
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1e5+7;
const int inf = 1e9+7;
typedef long long LL;
int n;
int a[maxn];
double k,ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+n);
if(n%2)
k=a[n/2+1];
else
k=(a[n/2]+a[n/2+1])/2;
for(int i=1;i<=n;i++)
ans+=fabs(a[i]-k);
printf("%.0f\n",ans);
return 0;
}
D.Sunscreen
题意:
有C
头奶牛,每头奶牛都要去晒太阳,晒太阳前要擦防晒霜,每头奶牛能接受的SPF的范围是[ Min[i],Max[i] ]
,有L
种防晒霜,给出SPF和瓶数,这里我用l[i]
表示SPF为i的防晒霜的瓶数。每头奶牛只能涂一瓶防晒霜,问最多有几头奶牛可以获得防晒?
题解:
贪心。
1.将Min[i]
按递减排序。然后对于每一头奶牛,从Max[i]~Min[i]
搜索防晒霜,存在的话就给这头奶牛。
2.将Max[i]
按递增排序。然后对于每一头奶牛,从Min[i]~Max[i]
搜索防晒霜,存在的话就给这头奶牛。
证明1(2同理):
若按上述排序排好,对于任意上下2头牛(黑色和红色)有2种情况
1.红色的Max小于黑色的Max
2.红色的Max大于等于黑色的Max
对于黑色的任意2瓶可选的防晒霜(假设SPFx
如果是(+,+),那么1人1瓶。
如果是(+,-),x给红色 y给黑色
如果是(-,-),黑色从x和y中任选1瓶
对于第二种情况跟(+,+)同理。
所以综合起来就是优先将SPF大的给前面的牛
也可以反过来,所有有2种贪心方式
代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 2500+7;
const int inf = 1e9+7;
typedef long long LL;
int C,L,ans;
struct Cow
{
int Min,Max;
bool operator < (const Cow a) const
{
if(Min==a.Min)
return Max>a.Max;
return Min>a.Min;
}
};
Cow c[maxn];
int l[1007];
int main()
{
scanf("%d %d",&C,&L);
for(int i=1;i<=C;i++)
scanf("%d %d",&c[i].Min,&c[i].Max);
for(int i=1;i<=L;i++)
{
int SPF,num;
scanf("%d %d",&SPF,&num);
l[SPF]+=num;
}
sort(c+1,c+1+C);
for(int i=1;i<=C;i++)
{
for(int j=c[i].Max;j>=c[i].Min;j--)
if(l[j])
{
l[j]--;
ans++;
break;
}
}
printf("%d\n",ans);
return 0;
}
E.递归实现排列型枚举
题意:
给你一个N,要求输出N的全排列数字串(1~N),按字典序从小到大排序
例如N=3
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
题解:
dfs+回溯
代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1e5+7;
const int inf = 1e9+7;
typedef long long LL;
int n;
int vis[15],ans[15],cnt;
void dfs(int num)
{
if(num==n+1)
{
for(int i=0;i<cnt;i++)
printf("%d ",ans[i]);
printf("\n");
return;
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
vis[i]=1;
ans[cnt++]=i;
dfs(num+1);
cnt--;
vis[i]=0;
}
}
}
int main()
{
scanf("%d",&n);
dfs(1);
return 0;
}
F.七夕祭
G.奇数码问题
题意:
给一个奇数N
(N<500),给出2个N*N
的棋盘A、B(0~N*N-1,0是空格)
问A能否通过移动到达B?
题解:
考察A、B的逆序对个数即可
奇数码在移动过程中整个棋盘的逆序对奇偶性不变
所以判断A、B的逆序对个数奇偶性是否相同
代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 3e5+7;
const int inf = 1e9+7;
typedef long long LL;
int n;
int a[maxn],temp[maxn],cnt;
LL ans;
void Merge(int l,int m,int r)
{
int p=l,q=m+1;
cnt=0;
while(p<=m && q<=r)
{
if(a[p]<=a[q])
temp[cnt++]=a[p++];
else
{
ans+=m+1-p;
temp[cnt++]=a[q++];
}
}
while(p<=m) temp[cnt++]=a[p++];
while(q<=r) temp[cnt++]=a[q++];
for(int i=0;i<cnt;i++)
a[i+l]=temp[i];
}
void Sort(int l,int r)
{
if(l==r)
return;
int m=(l+r)/2;
Sort(l,m);
Sort(m+1,r);
Merge(l,m,r);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
ans=0;
int num,len=0;
for(int i=0;i<n*n;i++)
{
scanf("%d",&num);
if(num)
a[++len]=num;
}
Sort(0,len);
len=0;
for(int i=0;i<n*n;i++)
{
scanf("%d",&num);
if(num)
a[++len]=num;
}
Sort(0,len);
if((ans)%2)
printf("NIE\n");
else printf("TAK\n");
}
return 0;
}
H.Genius ACM
I.Sumdiv
J.最短Hamilton路径