今天折腾的真是够呛,明天运动会就不去凑热闹了,老老实实A题看博客,下午集中精力打比赛了。
HDU—6024
题意:
在一条线上有很多教室,可以在教室建立糖果店,每个教室都有坐标和建立糖果店的费用。如果该教室没有建立糖果店,那么在这个教室左边的糖果店到此教室的距离为此教室的费用,问最小的修建糖果店的费用是多少。
注意在第一个点上一定要建立糖果店。
来自:https://blog.csdn.net/a1046765624/article/details/79878557
dp[i][j]为到第i个教室,前一个糖果店的位置是j,的最小费用是多少。
分两种,一是在这里修建糖果店。dp[i][i]=min(dp[i][i],dp[i-1][j]+v[i])(1<=j<i)。
二是不修建糖果店。dp[i][j]=min(dp[i][j],dp[i-1][j]+d[i]-d[j])(1<=j<i)。
还有一种思路:
来自:https://blog.csdn.net/STILLxjy/article/details/72514980
根据题目所给的时间,和题目的数据的大小,我们可以知道题目可以承受住时间复杂度为O(n^2)的算法。
并且每个教室只有两种方案,要么建超市,要么不建。这就很像是背包问题了,所以我们就想到了dp.
我们设dp[i][0]表示在教室i不建超市时前i个教室的费用和的最小值;dp[i][1]表示在教室i建超市时前i个教室的费用和的最小值
那么我们很快可以得出: dp[i][1] = min(dp[i-1][0],dp[i-1][1]) + ci
关于dp[i][0],由于可以承受住时间复杂度为O(n^2)的算法,那么我们就可以想到枚举离教室i最近的超市j的位置,然后取所有情况的最小值就可以了。
假设左边最近超市为j,那么教室j+1~教室i都不能建超市,所以教室j+1~教室i的费用分别为他们的位置到教室j之间的距离了。当前dp[i][0] = dp[j][1] +( 教室j+1~教室i的费用)
如果我们暴力求解,那么时间复杂度会变成O(n^3),会超时。但是我们会发现由于j是从大到小变化的,所以就可以用:t += (i - j) * (nodes[j+1].x - nodes[j].x);来记录教室j+1~教室i的费用和了。
关于 t += (i - j) * (nodes[j+1].x - nodes[j].x); 的解释:
比如我们要算x3 - x1 , x2 - x1的sum,那么由于保证了x是升序排列的,所以sum = (x3 - x2) + 2 * (x2 - x1).
const int maxn=3200;//第一种思路
long long dp[maxn][maxn];
struct poin
{
int x,v;
}a[maxn];
int cmp(poin q,poin w)
{
return q.x<w.x;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].x,&a[i].v);
}
sort(a+1,a+1+n,cmp);
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[1][1]=a[1].v;
for(int i=2;i<=n;i++)
{
dp[i][i]=dp[i][i-1]+a[i].v;
for(int j=1;j<i;j++)
{
dp[i][j]=min(dp[i][j],dp[i-1][j]+a[i].x-a[j].x);
dp[i][i]=min(dp[i][i],dp[i-1][j]+a[i].v);
}
}
long long sum=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
sum=min(sum,dp[n][i]);
}
printf("%lld\n",sum);
}
return 0;
}
HDU—6025
题意:
给出n个数,问去掉其中一个数ai,问gcd(a1,a2...ai-1,ai+1...an)最大是多少
找两个数组,处理前i个gcd的值和后i个数的gcd,然后暴力每一个i,maxn=max(maxn,gcd(a[i-1],b[i+1]));就可以了
ll gcd(ll x,ll y)
{
if(y==0) return x;
else return gcd(y,x%y);
}
ll a[100005],b[100005];
ll c[100005];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
ll n;
ll t;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&c[i]);
}
t=c[1];
for(int i=1;i<=n;i++)
{
t=gcd(t,c[i]);
a[i]=t;
}
t=c[n];
for(int i=n;i>=1;i--)
{
t=gcd(t,c[i]);
b[i]=t;
}
ll maxn=max(b[2],a[n-1]);
for(int i=2;i<=n-1;i++)
{
maxn=max(maxn,gcd(a[i-1],b[i+1]));
}
cout<<maxn<<endl;
}
}
HDU—6027 水题,不说了
HDU—6030
题意: 给出红蓝两种,然后排成一个字符串,要求在每一个长度为素数的区间里面是的r(red)的数量不小与b(blue)的数量;
其实写一下前几项就能发现规律f[i]=f[i-1]+f[i-3];f[2]=3;f[3]=4;f[4]=6;f[5]=9;f[6]=13;
然后构造矩阵,矩阵快速幂就可以了。
const LL mod=1e9+7;
struct Matrix
{
LL m[5][5];
};
LL n, c;
int ssize = 4;
Matrix Mul(Matrix a,Matrix b)
{
int i, j, k;
Matrix c;
for(i = 1; i <= ssize; i++)
{
for(j = 1; j <= ssize; j++)
{
c.m[i][j]=0;
for(k = 1; k <=ssize;k++)
{
c.m[i][j]+=(a.m[i][k]*b.m[k][j])%mod;
c.m[i][j]%=mod;
}
}
}
return c;
}
Matrix pow_mod(Matrix m,LL n)
{
Matrix b;
memset(b.m,0,sizeof(b.m));
for(int i=1;i<=4;i++)
{
b.m[i][i]=1;
}
while(n>0)
{
if(n&1) b=Mul(b,m);
n=n>>1;
m=Mul(m,m);
}
return b;
}
int main()
{
LL T,n,k;
scanf("%lld",&T);
while(T--)
{
scanf("%lld",&n);
if(n==2) {cout<<3<<endl;continue;}
if(n==3) {cout<<4<<endl;continue;}
if(n==4) {cout<<6<<endl;continue;}
else if(n==5) {cout<<9<<endl;continue;}
Matrix str;
str.m[1][1]=str.m[1][3]=str.m[3][2]=1;//构造矩阵快速幂
str.m[2][2]=str.m[2][4]=str.m[4][3]=1;
str.m[1][2]=str.m[1][4]=str.m[2][1]=0;
str.m[2][3]=str.m[3][1]=str.m[3][4]=0;
str.m[3][3]=str.m[4][4]=str.m[4][1]=0;
str.m[4][2]=0;
n-=5;
str=pow_mod(str,n);
LL ans=0;
ans=(str.m[1][1]*9%mod+str.m[1][2]*6%mod+str.m[1][3]*4%mod+str.m[1][4]*3%mod)%mod;
cout<<ans<<endl;
}
return 0;
}
HDU—6029
题目描述:一种所有结点都有边与之相连的匹配叫做完美匹配。现在你有N个结点,对于n-1个结点,你有两种操作:
(1)将这个结点与之前的所有结点都连一条边
(2)不进行操作;
来自:https://blog.csdn.net/weixin_39453270/article/details/80026538
题面分析:这是一道很有意思的思维题。首先要明确的一点是题目只要我们求是否可能是完美匹配,而不是让我们判断是否一定是完美匹配。对于每个结点,当我们要进行第一种操作时,即意味着我们这个结点可以与前面的任意一个结点进行匹配;而当我们进行第二种操作的时候,意味着这个结点是完全孤立的。因为每次操作1时,当前节点的匹配是任意的,考虑到这点,我们可以考虑使用队列去做,即当进行操作1的时候,队列深度减1,当操作为2时,将队列深度加1,最后判断队列是否非空即可。
int a[MAXN];
int n;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
scanf("%d",&a[i]);
}
if(n%2==1)printf("No\n");
else
{
a[1]=2;
int f=0;
int sum=0;
for(int i=n;i>=1;i--)
{
if(a[i]==1)sum++;
else if(a[i]==2)sum--;
if(sum<0){f=1;break;}
}
if(f)printf("No\n");
else printf("Yes\n");
}
}
return 0;
}
HDU—6020
题意:
官方题解:
这题更多算是想法题,有脑洞的同学可以各种乱搞。将问题转化成:求去掉K位数字后,不含前导零,且数字和是否能被三整除。按照预测,这里应该会汇聚本场最多的hack点。
我们设S0、S1、S2分别为原串上mod3=0、1、2数字的个数。 我们假定删除取模后为0、1、2的数字各A、B、C个,则显然有0<=A<=S0,0<=B<=S1,0<=C<=S2且K=A+B+C且Sum mod3=(A∗0+B∗1+C∗2)mod3=(S0∗0+S1∗1+S2∗2)mod3=bias。 枚举C的值,我们可得Bmod3=(bias−C∗2)mod3,A=K−B−C。如果有若干组A,B不逾界,可知这些(A,B,C)是在模意义下合法的解,但不一定满足没有前导零。
所以,对于【大于0的数】我们贪心地从后往前删除,对于0我们贪心地从前往后删除。
需要统计出:a3=第一个【mod3=0且非0的数】前0的个数(如果mod3=0且非0的数不存在,那么a3就取所有零的个数),E1=【第一个0前是否存在mod3=1的数】,E2=【第一个0前是否存在mod3=2的数】。
则以下情况满足任一种都能保证无前导零:A>=a3。B<S1且E1。C<S2且E2。
还需要加一种情况 满足k==n-1 也是yes
这道题其实我的思路和它差不多,就是在n个里面选n-k个加和为3的倍数的情况,先预处理后i个0,1,2,3出现的次数,然后暴力起点(s[i]!=0),接下来还是暴力所有情况。
我感觉可能会超时,明天还得写写看看
来自:https://blog.csdn.net/luricheng/article/details/69264096
代码:https://blog.csdn.net/luricheng/article/details/69264096
const int inf = 1e9 + 7;
const int N = 100000 + 5;
char ch[N];
int x[N];
bool solve(int n,int k)
{
for(int i=0;i<n;++i)
{
x[i]=ch[i]-'0';
x[i]%=3;
}
int s[3]={0,0,0};
int sum=0;
for(int i=0;i<n;++i)
{
s[x[i]]++;
sum+=x[i];
}
int a3=0;//>0&&mod3==0的第一数 前面的0的个数
bool e1,e2=e1=0;
for(int i=0;i<n;++i)
{
if(ch[i]!='0'&&x[i]==0) break;
if(x[i]==0) a3++;
if(x[i]==1) e1=1;
if(x[i]==2) e2=1;
}
for(int c=0;c<=s[2];++c)
{
int minb=((sum-2*c)%3+3)%3;
for(int b=minb;b<=s[1];b+=3)
{
int a=k-b-c;
if(a>=0&&a<=s[0])
{
if((a>=a3)||(e1&&b<s[1])||(e2&&c<s[2])) return 1;
if(k==n-1) return 1;
}
}
}
return 0;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k;
scanf("%d%d%s",&n,&k,ch);
if(solve(n,k)) cout<<"yes"<<endl;
else cout<<"no"<<endl;
}
return 0;
}