USACO2.2【统计,dp,模拟,位运算】

版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/82955692

正题


T1:序言页码 P r e f a c e N u m b e r i n g Preface Numbering

评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P1465


题目大意

1 n 1\sim n 的罗马数字每个字母出现的次数。


解题思路

先写一个表,表示这个位上出现哪个数字表示 10 n 10n 的字母出现次数和表示 5 10 n 5*10n 的字母出现的次数。
然后直接计算


code

#include<cstdio>
#include<string>
using namespace std;
const char f[8]={' ','I','V','X','L','C','D','M'};
const int I[11]={0,1,2,3,1,0,1,2,3,1};
const int V[11]={0,0,0,0,1,1,1,1,1,0};
int n,c[8];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        c[1]+=I[i%10];
        c[2]+=V[i%10];
        c[3]+=I[i%100/10]+(i%10==9);
        c[4]+=V[i%100/10];
        c[5]+=I[i%1000/100]+(i%100/10==9);
        c[6]+=V[i%1000/100];
        c[7]+=I[i/1000]+(i%1000/100==9);
    }
    for(int i=1;i<=7;i++)
      if(c[i]) printf("%c %d\n",f[i],c[i]);
}

T2:集合 S u b s e t S u m s Subset Sums

评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P1466


题目大意

求将 1 n 1\sim n 分解为两组且两组之和相等的方案数。


解题思路

f i , j f_{i,j} 表示前i个数,两组相差为j时的方案总数。
然后
f i , j = f i , j i + f i , j + i f_{i,j}=f_{i,|j-i|}+f_{i,j+i}
然后得出的答案要除2,因为两组是一样的。


code

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll n,sum,f[40][801];
int main()
{
    scanf("%lld",&n);
    sum=0;
    f[0][0]=1;
    for(ll i=1;i<=n;i++)
    {
        sum+=i;
        for(ll j=0;j<=sum;j++)
        {
            f[i][j]=f[i-1][abs(j-i)]+f[i-1][j+i];
        }
    }
    printf("%lld",f[n][0]/2);
}

T3:循环数 R u n a r o u n d N u m b e r s Runaround Numbers

评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P1467


题目大意

求比n大的第一个循环数(循环数就是每到达一个数字就往前走这个数字的步数,然后每个数字都走过一次)


解题思路

直接暴力往后加,然后看一下是不是循环数。


code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,now,k[10],a[10];
bool check(int x)
{
    memset(k,0,sizeof(k));
    int tmp=x,w=0;
    while(tmp)
    {
        w++;
        a[w]=tmp%10;
        if(k[a[w]]) return false;
        k[a[w]]=true;
        tmp/=10;
    }
    for(int i=1;i<=w/2;i++)
      swap(a[i],a[w-i+1]);
    memset(k,0,sizeof(k));
    tmp=1;
    for(int i=1;i<=w;i++)
    {
        if(k[a[tmp]]||!a[tmp]) return false;
        k[a[tmp]]=true;
        tmp=(tmp+a[tmp]-1)%w+1;
    }
    if(tmp!=1) return false;
    return true;
}//判断循环数
int main()
{
    scanf("%d",&n);
    now=n;
    while(true)
    {
        now++;
        if(check(now))
        {
            printf("%d",now);
            break;
        }
    }
}

T4:派对灯 P a r t y L a m p s Party Lamps

评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P1468


题目大意

有n个灯
有4种操作:
1.灯开着就关,关了就开
2.奇数号的取反
3.偶数号的取反
4.3k+1的灯取反
给出操作次数和某些灯的最终状态,求所有可能的状态

扫描二维码关注公众号,回复: 3488590 查看本文章

解题思路

我们可以发现其实灯就是6个一个的循环节。
然后一个东西操作过2次就没有用,然后1和2,3就等于按另一个
我们可以预处理除c=2可以做到的所有情况,然后c>2时也可以做到c=2时的效果


code

#include<cstdio>
using namespace std;
const int s[8]={0,14,21,27,36,42,49,63};//预处理可以做到的情况
int n,c,open,close,a[6],x;
bool flag;
void check(int x)
{
    if((s[x]&open)!=open||s[x]&close) return;
    int tmp=s[x],i=1;
    while(tmp) a[6-i]=tmp%2,tmp/=2,i++;
    for(int i=0;i<n;i++)
      printf("%d",a[i%6]);
    flag=true;
    printf("\n");
}//判断这种情况是否满足
int main()
{
    scanf("%d%d",&n,&c);
    while(scanf("%d",&x)&&x!=-1)
        x=6-(x-1)%6-1,open|=1<<x;//开启的等
    while(scanf("%d",&x)&&x!=-1)
        x=6-(x-1)%6-1,close|=1<<x;//关闭的灯
    if(c==0) check(7);
    if(c==1) check(0),check(2),check(3),check(5);
    //前两个特判
    if(c>=2) 
      for(int i=0;i<=7;i++) check(i);
    if(!flag) printf("IMPOSSIBLE");
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/82955692
今日推荐