https://vjudge.net/contest/311635#overview
D HihoCoder 1326
自己纠结了很久都不会写,因为特殊情况真的好多啊。。规律真的找不到啊。。。
但,这时候,其实。。早就应该想起来枚举的啊!!!特殊情况过多,又不担心复杂度的话,胡不枚举!!!?
前缀和处理一下0、1个数,然后枚举断点,就好啦!
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int T,cnt,s1[1050],s0[1050],l;
string s;
int main(){
cin >> T;
while(T --){
cin >> s;
l = s.length();
cnt = 1050;
memset(s1,0,sizeof s1);
memset(s0,0,sizeof s0);
if(s[0]=='1') s1[0] = 1;
else s0[0] = 1;
for(int i = 0;i < l;i ++){
if(s[i] == '0'){
s1[i] = s1[i-1];
s0[i] = s0[i-1]+1;
}
if(s[i] == '1'){
s1[i] = s1[i-1] + 1;
s0[i] = s0[i-1];
}
}
for(int i = 0;i < l; i ++){
cnt = min(cnt,s1[i-1]+s0[l-1]-s0[i]);
}
printf("%d\n",cnt);
}
return 0;
}
E HihoCoder 1327
我觉得很简单啊。。为什么大佬们都不写呢。。?
按最优方案使劲儿循环~
优先把大于一半的字典序小的放出来(判断和前一个是不是不一样!
如果上一种啥也没放,就按字典序小的,相同的判断
如果有一轮啥都没放,就return0说不行
```#include<bits/stdc++.h>
#define ll long long
using namespace std;
bool fl ;
int a[30];
char s[100200];
int ans[100200];
int main(){
scanf("%s",s);
int l = strlen(s),sum=strlen(s);
for(int i = 0;i < l;i ++){
a[s[i]-'a'] ++;
}
int k = 0,j;
ans[k] = -1;
for(int i = 0;i < l;i ++){
fl = false;
for(j = 0;j < 26;j ++){
if(a[j]*2 > sum && j != ans[k]){
ans[++k] = j;
a[j] --;
sum --;
fl = true;
break;
}
}
if(j == 26){
for(int j = 0;j < 26;j ++){
if(a[j] != 0 && j!=ans[k]){
ans[++k] = j;
a[j] --;
sum --;
fl = true;
break;
}
}
}
if(!fl){
cout <<"INVALID"<<endl;
return 0;
}
}
for(int i = 1;i <=l;i ++){
printf("%c",(char)('a'+ans[i]));
}
return 0;
}
F HihoCoder 1328
一个宽搜~vis数组里边记得加一维计算拿到钥匙的状态鸭!
n和m不要搞反啦!
感觉还是非常有趣哒!
查错可真是太难了www。。。。。。。。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int aa[150][150],n,m,k,a,b,c,d,kx[10],ky[10],mx[] = {0,1,0,-1},my[] = {1,0,-1,0};
bool inq[35][150][150];
char ch;
struct node{
int x,y,st,hk;
node(int _x,int _y,int _st){
x = _x;y = _y;st = _st;
hk = 0;
}
node(){
};
};
queue<node> mq;
inline bool havekey(node p){
if(p.hk & (1 << (aa[p.x][p.y]-1))){
//printf("key ! %d %d\n",p.hk,aa[p.x][p.y]);
return true;
}
return false;
}
inline void fk(node &p){
for(int i = 1;i <= k;i ++){
if(!(p.hk & (1 << (i-1)))&&p.x == kx[i] && p.y == ky[i]){
p.hk += 1<<(i-1);
//printf("get key %d in %d %d\n",i,p.x,p.y);
break;
}
}
}
int bfs(int x,int y){
node p=node(x,y,1),nt;
fk(p);
memset(inq,false,sizeof inq);
mq.push(p);
inq[p.hk][p.x][p.y] = true;
while(!mq.empty()){
p = mq.front();
mq.pop();
//cout <<p.x <<' '<< p.y <<' '<< p.hk << ' ' << p.st<<endl;
if(p.x == c && p.y == d) return p.st-1;
for(int i = 0;i <= 3;i ++){
nt.x = p.x+mx[i];
nt.y = p.y+my[i];
nt.hk = p.hk;
nt.st = p.st+1;
if(aa[nt.x][nt.y] != -1){
fk(nt);
if(aa[nt.x][nt.y] == 0 && !inq[nt.hk][nt.x][nt.y]){
inq[nt.hk][nt.x][nt.y] = true;
mq.push(nt);
//printf("push %d %d %d %d from %d %d %d %d\n",nt.x,nt.y,nt.hk,nt.st,p.x,p.y,p.hk,p.st);
}
else{
if(!inq[nt.hk][nt.x][nt.y] && havekey(nt)){
inq[nt.hk][nt.x][nt.y] = true;
mq.push(nt);
}
}
}
}
}
return -1;
}
int main(){
scanf("%d %d %d %d %d %d %d",&n,&m,&k,&a,&b,&c,&d);
a ++;b ++;c ++;d ++;
for(int i = 0;i <= n;i ++) aa[i][0] = aa[i][n+1] = -1;
for(int j = 0;j <= m;j ++) aa[0][j] = aa[n+1][j] = -1;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++){
scanf("\n%c",&ch);
if(ch == '.') aa[i][j] = 0;
else if(ch == '#') aa[i][j] = -1;
else aa[i][j] = (int)(ch-'A'+1);
}
// for(int i = 0;i <= m+1;i ++){
// for(int j = 0;j <= n+1;j ++) printf("%d",aa[i][j]);
// cout <<endl;
// }
for(int i = 1;i <= k;i ++){
scanf("%d%d",&kx[i],&ky[i]);
kx[i]++;ky[i]++;
}
cout << bfs(a,b) << endl;
return 0;
}
/*
4 5 2 0 0 0 3
...B.
.#...
.#...
.#...
3 0
3 3
*/
G HihoCoder 1469
我觉得很有必要总结一下这道题!它虽然看上去很简单,但是和dalao们交流的时候发现它有10000000000种做法。。。
大佬们还是太强啦orz。
- 看起来最dalao的方法——澜式dp;//其实我还没太懂,本蒟dp无能啊。
先遍历一遍,标记所有块的属性:1代表它上下左右都很fu,2代表它上下很fu,左右不行;3代表 它左右很fu,上下不行。
也就是这样的:
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
if(a[i][j]+1==a[i+1][j]&&a[i][j]+1==a[i][j+1])
b[i][j]=1;
else
if(a[i][j]+1==a[i+1][j])
b[i][j]=2;
else
if(a[i][j]+1==a[i][j+1])
b[i][j]=3;
}
需要满足一个神秘的列之间的关系:
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
if(b[i][j]==1)
f[i][j]=f[i][j-1]+1;
else
f[i][j]=1;
if(b[i][j]==1||b[i][j]==3)
c[i][j]=c[i][j-1]+1;
else
c[i][j]=1;
}//满足列之间的关系
然后她就有了个 dp:
for(j=1;j<=n;j++)
for(i=1;i<=n;i++)
{
if(b[i][j]==1)
g[i][j]=g[i-1][j]+1;
else
g[i][j]=1;
if(b[i][j]==1||b[i][j]==2)
d[i][j]=d[i-1][j]+1;
else
d[i][j]=1;
}//每一列的最大连续子段
最后遍历一遍,求最大值,就ok啦!
2. 看起来最蒟蒻的方法——snow的爆搜;
它虽然看上去不甚强大,但是由于一点点剪枝,其实还是相当好用的~
感觉不需要解释呢,嘿嘿嘿
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int a[1050][1050],n,i,j,ma;
bool vis[1050][1050];
void sea(int x,int y,int r){
bool ok = true;
ma = max(ma,r);
if(x+r > n || y+r > n) return;
for(int i = x;i <= x + r;i ++){
//vis[i][y+r] = true;
if(a[i][y+r] != a[i][y+r-1] +1 ){
ok = false;
for(int j = x;j <= i;j ++)
for(int k = y;k <= y+r-1; k ++)
//cout << j<<" " << k <<endl,
vis[j][k] = true;
break;
}
}
if(ok){
for(int j = y;j <= y + r;j ++){
//vis[x+r][j] = true;
if(a[x+r][j] != a[x+r-1][j] +1){
ok = false;
for(int i = x;i <= x+r-1;i ++)
for(int k = y;k <= j; k ++)
//cout << i<<" " << k <<endl,
vis[i][k] = true;
break;
}
}
}
if(ok) sea(x,y,r+1);
}
int main(){
scanf("%d",&n);
ma = 1;
memset(vis,0,sizeof vis);
for(i = 1;i <= n;i ++)
for(j = 1;j <=n;j ++)
scanf("%d",&a[i][j]);
for(i = 1;i <= n;i ++)
for(j = 1;j <=n;j ++)
if(!vis[i][j]) sea(i,j,1);
printf("%d\n",ma);
return 0;
}
- 看起来非常非常短的(而且很好理解)
(但有什么比爆搜更好理解呢)——zzy递推;
令zzy十分快乐的dp,因为他从开始读题到写完只用了十分钟,%%%。
#include <bits/stdc++.h>
using namespace std;
int n;
int f[1010][1010];
int gh[1010][1010];
bool check(int x,int y){
if (gh[x][y]==gh[x-1][y]+1&&gh[x][y]==gh[x][y-1]+1&&gh[x][y]==gh[x-1][y-1]+2)
return true;
return false;
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
cin>>gh[i][j];
memset(f,0,sizeof f);
for (int i=1;i<=n;i++){
f[1][i]=1;
f[i][1]=1;
}
for (int i=2;i<=n;i++){
for (int j=2;j<=n;j++){
if (check(i,j)) f[i][j]=f[i-1][j-1]+1;else f[i][j]=1;
}
}
int ans=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
ans=max(ans,f[i][j]);
cout<<ans<<endl;
return 0;
}