【USACO1.4】解题报告

版权声明:若希望转载,在评论里直接说明即可,谢谢! https://blog.csdn.net/SSL_ZYC/article/details/82953565

前言

本章主要考的是贪心和一些比较麻烦的模拟。难度相比上一章有很明显的提升。但是稍微想一下还是可以想出来的(第五题除外,看了一下题解的思路)。
USACO:http://train.usaco.org


1.4.2.Mixing Milk

思路:

很明显是一道贪心基础题。肯定是要先买每单位价钱最便宜的,然后再依次买贵的。这个不用证明了吧。。。

代码:

/*
ID:ssl_zyc2
TASK:milk
LANG:C++
*/

#include <cstdio>
#include <algorithm>
#define N 5100
using namespace std;

int n,m,ans;

struct node
{
	int p,d;
}a[N];

bool cmp(node x,node y)
{
	return x.p<y.p;
}

int main()
{
	freopen("milk.in","r",stdin);
	freopen("milk.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	 scanf("%d%d",&a[i].p,&a[i].d);
	sort(a+1,a+1+m,cmp);  //以价格排序
	for (int i=1;i<=m;i++)
	 if (a[i].d<n)  //可以全部买完
	 {
	 	n-=a[i].d;  //全部买完
	 	ans=ans+a[i].d*a[i].p;
	 }
	 else 
	 {
	 	ans=ans+a[i].p*n;  //将不够的部分买完
	 	break;
	 }
	printf("%d\n",ans);
	return 0;
}

1.4.3.Barn Repair

思路:

首先假设买一块长度为第 n n 个牛棚 - 1 1 个牛棚的木板,然后我们可以试着把这块木板分成 k k 块(也就是切割 k 1 k-1 次!),那么最优的肯定是先将两个牛棚之间距离最大的部分割掉,然后割掉第二大的。。。以此类推。
要特判可用木板数 &gt; &gt; 牛棚数的情况。

代码:

/*
ID:ssl_zyc2
TASK:barn1
LANG:C++
*/	

#include <cstdio>
#include <algorithm>
#define N 250
using namespace std;

int n,m,k,a[N],b[N],sum;

bool cmp(int x,int y)
{
	return x>y;
}

int main()
{
	freopen("barn1.in","r",stdin);
	freopen("barn1.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	if (n>=k)
	{
		printf("%d\n",k);
		return 0;
	}
	for (int i=1;i<=k;i++)
	 scanf("%d",&a[i]);
	sort(a+1,a+1+k);  
	sum=a[k]-a[1]+1;  //一块木板
	for (int i=1;i<k;i++)
	 b[i]=a[i+1]-a[i];  //求出任意两个相邻的牛棚之间的距离
	sort(b+1,b+k,cmp);
	for (int i=1;i<n;i++)  //只能切n-1次!!!
	 sum=sum-b[i]+1;
	printf("%d\n",sum);
	return 0;
}

1.4.5.Prime Cryptarithm

思路:

由于每一个 “*” 只可能是 0 0 9 9 中的任意一个数字,那么我们可以枚举乘数里面的五个 &quot; &quot; &quot;*&quot; 分别是多少,然后就可以推出中间的六个 &quot; &quot; &quot;*&quot; 的值和答案的四个 &quot; &quot; &quot;*&quot; 的值。那么久判断每一个值是否合法即可。
时间复杂度: O ( n 5 ) O(n^5)

代码:

/*
ID:ssl_zyc2
TASK:crypt1
LANG:C++
*/

#include <cstdio>
#include <iostream>
using namespace std;

int n,a,b,ans;
bool p[11];

bool check(int x,int y)
{
	y++;
	while (x)
	{
		if (!p[x%10]) return 0;
		x/=10;
		y--;
		if (!y) return 0;
	}
	return 1;
}

int main()
{
	freopen("crypt1.in","r",stdin);
	freopen("crypt1.out","w",stdout);
	cin>>n;
	int x;
	for (int i=1;i<=n;i++)
	{
		cin>>x;
		p[x]=1;
	}
	for (int i=1;i<=9;i++)
	 if (p[i])
	  for (int j=0;j<=9;j++)
	   if (p[j])
	    for (int k=0;k<=9;k++)
	     if (p[k])
	      for (int l=1;l<=9;l++) 
	       if (p[l])
	        for (int q=0;q<=9;q++)  //枚举5个*
	         if (p[q])
	          if (check((i*100+j*10+k)*l,3))
	           if (check((i*100+j*10+k)*q,3))  //判断中间的六个*
	            if (check((i*100+j*10+k)*(l*10+q),4))  //判断积中的四个*
	             ans++;
	printf("%d\n",ans);
	return 0;
}

1.4.6.Combination Lock

思路:

暴力模拟。很简单吧。。。
小心数组越界。

代码:

/*
ID:ssl_zyc2
TASK:combo
LANG:C++
*/

#include <cstdio>
#define N 120
using namespace std;

int n,sum,a,b,c,d,e,f;
bool p[N][N][N];

int main()
{
	freopen("combo.in","r",stdin);
	freopen("combo.out","w",stdout);
	scanf("%d%d%d%d%d%d%d",&n,&a,&b,&c,&d,&e,&f);
	for (int i=a-2;i<=a+2;i++)
	 for (int j=b-2;j<=b+2;j++)
	  for (int k=c-2;k<=c+2;k++)  //第一个密码
	   if (!p[(i+n-1)%n+1][(j+n-1)%n+1][(k+n-1)%n+1])
	   {
	   	  p[(i+n-1)%n+1][(j+n-1)%n+1][(k+n-1)%n+1]=1;
	   	  sum++;
	   }
	for (int i=d-2;i<=d+2;i++)
	 for (int j=e-2;j<=e+2;j++)
	  for (int k=f-2;k<=f+2;k++)  //第二个密码
	   if (!p[(i+n-1)%n+1][(j+n-1)%n+1][(k+n-1)%n+1])
	   {
	   	  p[(i+n-1)%n+1][(j+n-1)%n+1][(k+n-1)%n+1]=1;
	   	  sum++;
	   }
	printf("%d\n",sum);
	return 0;
}

1.4.7.Wormholes

思路:

有点难度啊。
由于贝西只能往下走,那么她所在位置的下面第一个虫洞就是会被传走的地方。
那么设 n e x t [ i ] next[i] 表示在 i i 下面的第一个虫洞( y y 坐标要相同, x x 坐标严格小于且最近)。然后就深搜,搜出配对的方案后就判断是否成立。成立就记录。

代码:

/*
ID:ssl_zyc2
TASK:wormhole
LANG:C++
*/

#include <cstdio>
using namespace std;

int f[15],next[15],n;

int check()
{
	for (int i=1;i<=n;i++)
	{
		int u=i;
		for (int j=1;j<=n;j++)
		 u=next[f[u]];  //是否死循环
		if (u) return 1;
	}
	return 0;
}

int dfs(int s)
{
	if (s==n/2) return check();  //搜好了
	int x;
	for (x=1;x<=n;x++)
	 if (!f[x]) break;  //没有配对
	int ans=0;
	for (int i=1;i<=n;i++)
	 if (i!=x&&!f[i])
	 {
	 	f[x]=i;
	 	f[i]=x;
	 	ans+=dfs(s+1);
	 	f[x]=0;
	 	f[i]=0;
	 }
	return ans;
}

int main()
{
	freopen("wormhole.in","r",stdin);
	freopen("wormhole.out","w",stdout);
	scanf("%d",&n);
	int x[15],y[15];
	for (int i=1;i<=n;i++)
	 scanf("%d%d",&x[i],&y[i]);
	for (int i=1;i<=n;i++)
	 for (int j=1;j<=n;j++)
	  if (i!=j)
	   if (y[i]==y[j]&&x[j]>x[i]&&(!next[i]||x[next[i]]>x[j]))
	    next[i]=j;
	printf("%d\n",dfs(0));
	return 0;
}

1.4.8.Ski Course Design

思路:

由于山峰高度只有 100 100 ,那么就可以枚举山峰最矮高度,然后再枚举每个山峰,如果不够或高出那么就计算所需费用。然后将所有费用取最小值即可。

代码:

/*
ID:ssl_zyc2
TASK:skidesign
LANG:C++
*/

#include <cstdio>
#include <algorithm>
#define N 1010
using namespace std;

int h[N],sum,n,ans;

int main()
{
	freopen("skidesign.in","r",stdin);
	freopen("skidesign.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	 scanf("%d",&h[i]);
	sort(h+1,h+1+n);
	if (h[n]-h[1]<=17)  //特判,本来就不用
	{
		printf("0\n");
		return 0;
	}
	ans=1e9;
	for (int i=h[1];i<=h[n]-17;i++)  //枚举最低高度
	{
		sum=0;
		for (int j=1;j<=n;j++)  //枚举山峰
	  	 if (h[j]<i) sum=sum+(i-h[j])*(i-h[j]);
	   	  else if (h[j]>i+17) sum=sum+(h[j]-i-17)*(h[j]-i-17);
	   	if (sum<ans) ans=sum;
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/SSL_ZYC/article/details/82953565
今日推荐