题目已存档至硬盘
万能遥控器
第一道就是毒瘤模拟,TAT.先不改了,记录一下我的代码和标程吧.
#include <bits/stdc++.h>
using namespace std;
int n;
char ip[100][50],ans[100][50];
string s;
inline int read()
{
int s=1,x=0;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')s=0;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();}
return s?x:-x;
}
bool special(int &len)
{
for(int i=0;i<len-2;i++)
{
if(s[i]==':'&&s[i+1]==':'&&s[i+2]==':')
return true;
}
return false;
}
void solve()
{
for(int j=0;j<100;j++)
{
for(int k=0;k<50;k++)
{
ip[j][k]='x';
ans[j][k]='x';
}
}
cin>>s;
int len=s.length(),flag=-1,p=0,p_group=-1;
if(special(len))
{
cout<<"INVALID
";
return;
}
for(int j=0;j<len;j++)
{
if(j<len-1&&s[j]==':'&&s[j+1]==':')
{
if(flag==-1)
flag=p;
else
{
cout<<"INVALID
";
return;
}
p++;
j++;
p_group=-1;
}
else if(s[j]==':')
{
p++;
p_group=-1;
}
else
ip[p][++p_group]=s[j];
}
if((p<7&&flag==-1) || p>7 || (p==6 && flag>=0))
{
cout<<"INVALID
";
return;
}
for(int j=0;j<=p;j++)
{
int tot=0;
for(int k=0;k<50;k++)
{
if(ip[j][k]=='x') break;
else if((ip[j][k]>='0'&&ip[j][k]<='9') || (ip[j][k]>='A'&&ip[j][k]<='F'))
{
tot++;
}
else
{
cout<<"INVALID
";
return;
}
}
if(tot>4)
{
cout<<"INVALID
";
return;
}
else
{
for(int k=0;k<4-tot;k++)
{
ans[j][k]='0';
}
for(int k=4-tot;k<4;k++)
{
ans[j][k]=ip[j][k-4+tot];
}
}
}
for(int j=0;j<=p;j++)
{
for(int k=0;k<4;k++)
{
if(ans[j][k]=='x')
{
cout<<"INVALID
";
return;
}
cout<<ans[j][k];
}
if(j!=p) cout<<':';
if(j==flag)
{
for(int k=1;k<=(7-p);k++)
{
printf("0000");//bug,but solved
if(k+j!=7) putchar(':');
}
}
}
cout<<endl;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
solve();
}
return 0;
}
137行,学OI来写的最多的一次.
标程:
#include <bits/stdc++.h>
using namespace std;
char s[100], a[100];
int main()
{
freopen( "ipv6.in", "r", stdin ); freopen( "ipv6.out", "w", stdout ); int n;
scanf( "%d
", &n );
for ( int ii = 1; ii <= n; ii++ )
{
memset( s, '0', sizeof(s) );
memset( a, '0', sizeof(a) );
char c;
int p1 = 0, p2 = 0, p3 = 0, p4 = 0, k = 0, point = 0;
while ( (c = getchar() ) != EOF && c != '
' )
{
s[++k] = c;
if ( c == ':' )
{
p1 = 0; p2++;
}else p1++;
if ( p1 > 4 || p2 > 7 )
p4 = 1;
if ( c == ':' && s[k - 1] == ':' )
{
p3++; point = k;
}
if ( p3 > 1 )
p4 = 1;
}
if ( !p3 && p2 < 7 )
p4 = 1;
if ( p3 && p2 == 7 && point != 2 && point != k )
p4 = 1;
for ( int i = 1; i <= k; i++ )
a[i] = s[k - i + 1];
if ( p4 )
{
printf( "INVALID
" ); continue;
}
int q = 0;
p1 = 0;
for ( int i = 1; i <= k; i++ )
{
s[++q] = a[i];
p1++;
if ( a[i] == ':' )
{
q--;
while ( p1 < 5 )
{
s[++q] = '0';
p1++;
}
s[++q] = ':';
p1 = 0;
if ( a[i + 1] == ':' )
{
while ( p2 <= 7 )
{
for ( int j = 1; j <= 4; j++ )
s[++q] = '0';
s[++q] = ':';
p2++;
}
q--;
p1 = 4;
}
}
}
for ( int i = 39; i >= 1; i-- )
printf( "%c", s[i] );
cout << endl;
}
return(0);
}
收藏钻石
这道题很有意思呀,乍一看以为是二分答案,结果要分成2个陈列架.然后想到用一块板子将钻石们分开,左右2边分别二分答案,但这样复杂度是 O ( n 2 l o g n ) O(n^2 log n) O(n2log n).考虑优化,首先第一层枚举板子的循环肯定不能再优化了,难道优化二分答案?
结果真的是优化二分答案,考场上想出了一种DP方案.
若
m
表
示
所
有
满
足
a
[
i
]
−
a
[
i
−
m
]
≤
k
的
m
的
最
小
值
,
则
f
[
i
]
=
m
a
x
(
f
[
i
−
1
]
,
m
+
1
)
若m表示所有满足a[i]-a[i-m]le k的m的最小值,则\f[i]=max(f[i-1],m+1)
若m表示所有满足a[i]−a[i−m]≤k的m的最小值,则f[i]=max(f[i−1],m+1)
然后正向和反向分别DP一次,再插板,即可求出最终答案.
#include <bits/stdc++.h>
using namespace std;
const int N=5e4+5;
int n,k,s[N],ans,l[N],r[N];
inline int read()
{
int s=1,x=0;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')s=0;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();}
return s?x:-x;
}
int main()
{
freopen("diamond.in","r",stdin);
freopen("diamond.out","w",stdout);
n=read();
k=read();
for(int i=1;i<=n;i++)
{
s[i]=read();
}
sort(s+1,s+n+1);
l[1]=r[n]=1;
l[0]=r[n+1]=0;
for(int i=2;i<=n;i++)
{
int x=l[i-1];
while(s[i]-s[i-x+1]<=k&&i-x>=0)
{
x++;
}
l[i]=max(l[i-1],x-1);
// cout<<l[i]<<' ';
}
// cout<<endl;
for(int i=n-1;i>=1;i--)
{
int x=r[i+1];
while(s[i+x-1]-s[i]<=k&&i+x<=n+1)
{
x++;
}
r[i]=max(r[i+1],x-1);
// cout<<r[i]<<' ';
}
// cout<<endl;
for(int i=0;i<=n;i++)
{
ans=max(ans,l[i]+r[i+1]);
}
cout<<ans<<endl;
return 0;
}
sequence
最难的一道DP.看了题解,还结合了另外的算法,于是先去把字符串哈希做了.
决定以后hash的mod就用1e9+7和1e7+7,base为131
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e4+5,mod=1e9+7,base=131;
int n,ans;
ll a[N];
string s;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>s;
int len=s.length();
for(int j=0;j<len;j++)
{
a[i]=(a[i]*base+(ll)s[j])%mod;
}
}
sort(a+1,a+n+1);
for(int i=1;i<n;i++)
{
if(a[i]==a[i+1]) ans++;
}
cout<<n-ans;
return 0;
}
回到这道题,我的思考过程落脚于将数列分开的"切点".切点前数列的长度一定小于等于切点后数列的长度.然后就开始愉快的正向DFS模拟了,每次可以将剩下没切的数列切下一块,但切的长度,要么是小于等于剩下长度的二分之一,要么是等于剩下的长度(切完).中途会比较本次切的部分与上次切的部分的大小.
一份代码就出炉了:
#include <bits/stdc++.h>
using namespace std;
const int N=5e3+5,mod=1e9+7;
int n,ans;
int s[N];
bool cmp(const int &l1,const int &r1,const int &l2,const int &r2)//if l1 to r1>l2 to r2
{
if(r1-l1>r2-l2)
return true;
else if(r1-l1==r2-l2)
{
for(int i=l1;i<=r1;i++)
{
if(s[i]<s[l2+i-l1]) return false;
if(s[i]==s[l2+i-l1]&&i==r1) return false;
}
return true;
}
else return false;
}
void solve(int l,int r,int pre_l,int pre_r)
{
if(l==r)
{
(ans+=1)%=mod;
return;
}
if(s[l]==0) return;
int pre_size=pre_l-pre_r+1;
for(int i=pre_size;i<=((r-l+1)/2);i++)
{
if(cmp(l,l+i-1,pre_l,pre_r))
solve(l+i,r,l,l+i-1);
}
if(cmp(l,r,pre_l,pre_r))
{
solve(r,r,l,r);
}
}
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%1d",&s[i]);
}
solve(1,n,0,0);
cout<<ans%mod;
return 0;
}
首先DFS不是正解DP,复杂度就被卡掉了,其次正确性还出了问题,另外,根本就没考虑到cmp函数会是正解程序优化的着重部分,复杂度Up.
如果复杂度过高,n=10的部分解也有30分,也是个不错的选择,但因为正确性问题,最终只得到了10分.
开始正解分析:
本题的答案需要mod 1e9+7,显然考虑DP求总方案数.定义f[i][j]为取[i,j]为最后一段的总方案数,那么f[i][j]可以由f[k][i-1]转移得到(k<i-1).