前言
时间不够,暴力来凑(考试直接上暴力,水了50分)
题目
Time Limits: 1000 ms Memory Limits: 65536 KB
学生都很喜欢灌水,第一天只有Alice给她的每个朋友灌了一次水,从第二天开始,所有学生(包括Alice)将会有规律地去灌水:
•如果前一天被灌了奇数次的水,他们将会给每个朋友灌一次水;
•如果前一天被灌了偶数次的水,他们将会给每个朋友灌两次水。
学生编号为1到N,Alice为1号,学生之间的朋友关系会给出。
计算H天后一共灌了几次水。
Input
输入一行包含两个整数N和H(1<=N<=20,1<=H<=10^9),表示学生数和天数。
接下来N行,每行包含N个‘0’或‘1’,(A,B)处的字符表示A和B的关系,‘1’表示是朋友关系,‘0’表示不是。注意自己和自己不是朋友关系,输入保证该矩阵是对称的。
Output
输出H天后一共灌水的数量。
Sample Input
输入1: 4 1 0110 1001 1001 0110 输入2: 4 2 0110 1001 1001 0110 输入3: 5 3 01000 10110 01000 01001 00010
Sample Output
输出1: 2 输出2: 14 输出3: 26
【样例解释】
样例2中,第一天Alice灌了2次水,第二天学生1和学生4给学生2和学生3都灌了2次水,而学生2和学生3给学生1和学生4各灌水1次,2天一共灌了12次水。
【数据范围】
50%的数据 H<=1000。
分析
简单描述(毕竟我不太会打):状态压缩(奇数1,偶数0),01二进制数列转成十进制+周期(找循环)
具体解析、实现+特别鸣谢:https://blog.youkuaiyun.com/ha_ing/article/details/98498497
考试暴力代码
(居然有50分?!)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=20;
int a[MAXN+5][MAXN+5];
int tmp[MAXN+5],del[MAXN+5];
char b[MAXN+5][MAXN+5];
int n,h;
ll ans;
void water(int id)
{
memset(tmp,0,sizeof(tmp));
if(id==1)
{
for(int i=1;i<=n;i++)
if(a[1][i]==1&&i!=1)
tmp[i]++;
}
else
{
for(int i=1;i<=n;i++)
{
int t;
if(del[i]%2==0)
t=2;
else
t=1;
for(int j=1;j<=n;j++)
if(j!=i&&a[i][j]==1)
tmp[j]+=t;
}
}
for(int i=1;i<=n;i++)
{
ans+=(ll)tmp[i];
del[i]=tmp[i];
}
}
int main()
{
scanf("%d%d",&n,&h);
for(int i=1;i<=n;i++)
{
scanf("%s",b[i]+1);
for(int j=1;j<=n;j++)
a[i][j]=b[i][j]-'0';
}
for(int i=1;i<=h;i++)
water(i);
printf("%lld",ans);
return 0;
}
//直接暴力,加油
AC代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e6+5e4;
ll f[MAXN+5],w[MAXN+5],d[25][25],er[25],cnt[25];
ll n,h,t,res,ans,pre;
bool flag=false;
char a[25];
int main()
{
scanf("%lld%lld",&n,&h);
for(int i=1;i<=n;i++)
{
scanf("%s",a+1);
for(int j=1;j<=n;j++)
if(a[j]=='1')
d[i][++cnt[i]]=j;//累计i的朋友
}
er[1]=1;
for(int i=2;i<=n;i++)
er[i]=er[i-1]*2;//预处理,方便将二进制转十进制,为状压铺路
t=0;
for(int i=1;i<=cnt[1];i++)//从1号(Alice)开始处理
t+=er[d[1][i]];//t是二进制转十进制的结果
res=cnt[1];//将第一天的灌水次数加入答案
w[t]=res;//情况t的灌水次数
f[t]=1;//第一天(情况t)的灌水奇偶情况
for(int k=2;k<=h;k++)//枚举天数
{
pre=t;
t=0;
for(int i=1;i<=n;i++)
{
if(pre&er[i])//是奇数
{
for(int j=1;j<=cnt[i];j++)
if(t&er[d[i][j]])
t-=er[d[i][j]];
else
t+=er[d[i][j]];
res+=cnt[i];
}
else//是偶数
res+=2*cnt[i];
}
if(!f[t])//出现新状态
{
f[t]=k;//灌水奇偶情况t出现在第k天
w[t]=res;//情况t的灌水次数
}
else//开始循环,发现规律
{
ans=w[t]+(h-f[t])/(k-f[t])*(res-w[t]);
h=(h-f[t])%(k-f[t]);
flag=true;
break;
}
}
if(flag)
{
res=0;
for(int k=1;k<=h;k++)
{
pre=t;
t=0;
for(int i=1;i<=n;i++)
{
if(pre&er[i])
{
for(int j=1;j<=cnt[i];j++)
if(t&er[d[i][j]])
t-=er[d[i][j]];
else
t+=er[d[i][j]];
res+=cnt[i];
}
else
res+=2*cnt[i];
}
}
}
printf("%lld",ans+res);
return 0;
}
番外
自己去找写博客的大佬问了些细节问题: