【USACO2.1】解题报告
前言
USACO2.1主要内容是图论。其中有几道题是要用深搜做的。而另外几道题则是利用模拟的方法来完成简单图论。
总体来说难度还是比较小的,算是最基础的算法吧。
UASCO:http://train.usaco.org
2.1.3.The Castle
2.1.4.Ordered Fractions
思路:
可以枚举分子和分母,然后判断他们的最小公约数是不是(其实就是判断是否互质)。如果是的话就将这个分数加入到结构体里面,储存三个变量:
- 分子
- 分母
- 这个分数的值(实数类型)
然后按照分数值排序输出即可。
代码:
/*
ID:ssl_zyc2
TASK:frac1
LANG:C++
*/
#include <cstdio>
#include <algorithm>
using namespace std;
int n,sum;
struct node
{
int z,m;
double num;
}a[160*160+10];
bool cmp(node x,node y)
{
return x.num<y.num;
}
int main()
{
scanf("%d",&n);
a[++sum].z=0;
a[sum].m=1;
a[sum].num=0; //分子为0的唯一情况先打上
for (int i=1;i<=n;i++)
for (int j=1;j<=i;j++)
if (__gcd(i,j)==1) //互质
{
a[++sum].z=j;
a[sum].m=i;
a[sum].num=(double)j/(double)i/1.0;
}
sort(a+1,a+1+sum,cmp); //排序
for (int i=1;i<=sum;i++)
printf("%d/%d\n",a[i].z,a[i].m);
return 0;
}
2.1.5.Sorting a Three-Valued Sequence
思路:
这道题有点恶心。
读入,将排好序后为,然后从前往后扫,如果就说明这个的位置是错误的。那么要分情况。
- 先到本来应该在的位置,如果这个位置中
有为本来应该在a[i]位置的数字
,那么就交换两个数字,一次交换就满足了两个数字。 - 如果这个位置中
没有为本来应该在a[i]位置的数字
,那么就随便找一个要到位置来的数字交换,一次交换只满足了一个数字。
模拟即可。
代码:
/*
ID:ssl_zyc2
TASK:sort3
LANG:C++
*/
#include <cstdio>
#include <algorithm>
using namespace std;
int n,a[1010],b[1010],cnt;
bool ok;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
for (int i=1;i<=n;i++)
if (a[i]!=b[i]) //不相同
{
ok=false;
for (int j=i+1;j<=n;j++)
if (a[i]==b[j]&&a[j]==b[i]) //情况1
{
swap(a[i],a[j]);
cnt++;
ok=true;
break;
}
if (!ok) //情况2
for (int j=n;j>i;j--)
if (a[j]==b[i])
{
swap(a[i],a[j]);
cnt++;
break;
}
}
printf("%d\n",cnt);
return 0;
}
2.1.6.Healthy Holsteins
思路:
深搜枚举每一个饲料,判断买或者不买。最终取答案最小值即可。
当然如果你英勇一点的话就随机选择那些饲料,多随机几次取最有答案。如果运气好的话应该也是可以过的。
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
int n,m,need[30],sum[30],f[20][30],used[30];
struct node
{
int ans;
int used[30];
}ans;
bool check(int k)
{
for (int i=1;i<=n;i++)
if (sum[i]<need[i]) return 0;
if (k<ans.ans) return 1;
if (k>ans.ans) return 0; //取最小值
sort(used+1,used+1+k);
for (int i=1;i<=k;i++)
if (used[i]<ans.used[i]) return 1;
else if (used[i]>ans.used[i]) return 0;
}
void dfs(int x,int s)
{
if (x>m)
{
if (check(s)) //成立
{
ans.ans=s;
for (int i=1;i<=s;i++)
ans.used[i]=used[i];
sort(ans.used+1,ans.used+1+s); //排序
}
return;
}
dfs(x+1,s);
used[s+1]=x;
for (int i=1;i<=n;i++)
sum[i]+=f[x][i];
dfs(x+1,s+1);
used[s+1]=0;
for (int i=1;i<=n;i++)
sum[i]-=f[x][i];
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&need[i]);
scanf("%d",&m);
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++)
scanf("%d",&f[i][j]);
ans.ans=1e9;
dfs(1,0);
printf("%d",ans.ans);
for (int i=1;i<=ans.ans;i++)
printf(" %d",ans.used[i]);
printf("\n");
return 0;
}
//我这种方法很麻烦,应该有更简单的方法的。
2.1.7.Hamming Codes
思路:
深搜枚举每一位数是还是,搜完一个答案后将它和前面的所有答案进行比较,如果都满足要求就记录下这个答案。最后输出时再转换成十进制即可。
代码:
/*
ID:ssl_zyc2
TASK:hamming
LANG:C++
*/
#include <cstdio>
using namespace std;
int n,m,k,ans[70][10],a[10],sum;
void check()
{
for (int i=1;i<=sum;i++)
{
int cnt=0;
for (int j=1;j<=m;j++)
if (a[j]!=ans[i][j]) cnt++; //不同的位数个数
if (cnt<k) return; //不满足要求
}
sum++;
for (int i=1;i<=m;i++)
ans[sum][i]=a[i]; //记录答案
}
void dfs(int x)
{
if (x>m) //选择完了
{
check();
return;
}
dfs(x+1); //这一位是0
if (sum>=n) return;
a[x]=1;
dfs(x+1); //这一位是1
if (sum>=n) return;
a[x]=0;
}
int main()
{
freopen("hamming.in","r",stdin);
freopen("hamming.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
sum=1;
dfs(1);
for (int i=1;i<=n;i++)
{
int s=0,l=0;
for (int j=m;j>=1;j--)
{
s+=(1<<l)*ans[i][j]; //转换成十进制
l++;
}
if (i%10==1) printf("%d",s);
else if (i%10==0) printf(" %d\n",s);
else printf(" %d",s); //USACO神奇的输出要求
}
if (n%10) printf("\n"); //USACO神奇的输出要求(末尾必须换行)
return 0;
}