1.http://codeforces.com/problemset/problem/946/D
/*
***************************************************
刚开始写的是贪心,WA了
正解是DP
val[i][j]表示第i层删了j个最小花费
f[i][j]表示到了第i层,删了j个,最小花费
f[i][j]=min(f[i][j],f[i-1][p]+val[i][j-p]);
把每一层看作一组,有点像分组背包(好吧,不能分组背包,每一层都有贡献,实测WA了)
***************************************************
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int N=1000;
struct node{int pos,val,lc,rc,shit;};
int n,m,k,tot=0;
char a[N][N];
priority_queue<node> q;
bool operator<(const node &x,const node &y){return x.val<y.val||(x.val==y.val&&x.shit>y.shit);}
void find_s_t(char *a,int &l,int &r){
for(int i=1;i<=m;i++)if(a[i]=='1'){l=i;break;}
for(int i=m;i>=1;i--)if(a[i]=='1'){r=i;break;}
}
void work(char *a,int pos,int st,int ed,int k){
if(st==ed){q.push((node){pos,1,0,0});return;}
if(st==0&&ed==0)return;
int i=st+1;for(i;i<=m;i++)if(a[i]=='1')break;
int j=ed-1;for(j;j>=1;j--)if(a[j]=='1')break;
if(ed-j>=i-st){
int sum=0;
for(int k=j;k>=1;k--)if(a[k]=='0')break;else sum++;
q.push((node){pos,ed-j,st,j,sum});
}else{
int sum=0;
for(int k=i;k<=m;k--)if(a[k]=='0')break;else sum++;
q.push((node){pos,i-st,i,ed,sum});
}
if(k==1)tot+=ed-st+1;
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d%d",&n,&m,&k);
rep(i,1,n)scanf("%s",a[i]+1);
rep(i,1,n){int st=0,ed=0;find_s_t(a[i],st,ed);work(a[i],i,st,ed,1);}
rep(i,1,k){
node x=q.top();q.pop();
if(x.lc==0&&x.rc==0){tot-=1;continue;}
tot-=x.val;
work(a[x.pos],x.pos,x.lc,x.rc,0);
}
cout<<tot;
return 0;
}
*/
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=510;
int val[N][N],f[N][N],SL[N],SR[N],sum[N],ans=1e9,n,m,k;
char a[N][N];
int main()
{
//freopen("a.in","r",stdin);
scanf("%d%d%d",&n,&m,&k);
rep(i,1,n)scanf("%s",a[i]+1);
memset(f,0x3f,sizeof(f));
memset(val,0x3f,sizeof(val));
rep(i,1,n){
rep(j,1,m)SL[j]=SL[j-1]+(a[i][j]=='1');
per(j,m,1)SR[j]=SR[j+1]+(a[i][j]=='1');
rep(j,1,m)sum[i]+=(a[i][j]=='1');
rep(l,1,m){
rep(r,l,m){
if(l==r)val[i][SL[l]+SR[r+1]]=min(val[i][SL[l]+SR[r+1]],0);
if(r==l&&a[i][r]=='0')
val[i][SL[l-1]+SR[r+1]]=min(val[i][SL[l-1]+SR[r+1]],0);
else
val[i][SL[l-1]+SR[r+1]]=min(val[i][SL[l-1]+SR[r+1]],r-l+1);
}
}
}
rep(i,0,k)f[0][i]=0;
rep(i,1,n)rep(p,0,sum[i])per(j,k,p)
f[i][j]=min(f[i][j],f[i-1][j-p]+val[i][p]);
cout<<f[n][k];
return 0;
}
2.https://www.lydsy.com/JudgeOnline/problem.php?id=1226
/*
感觉是状压神题
f[i][S][k]表示i之前的人都吃过了,i及i之后的人吃饭的状态是S,上一个吃饭的人与i的距离是k 的最小代价
我们可以发现这个状态设计非常的好(QAQ)首先考虑i是阶段,肯定是递增的,[S][k]是每个状态,与答案有着密切的关系
然后我们考虑如何转移
其实和顺序有关的问题可以用状态压缩,因为我们考虑一个集合,加入元素的顺序不同,显然答案也不同,所以我们每一个状态
保存的就是加入 属于集合S的元素 后的最优解
然后我们考虑如何转移(QAQ)
对于一个状态,那我们考虑肯定枚举i后面的人让他先吃饭,于是就
f[i][S|(1<<r)][r]=min(f[i][S|(1<<r)][r],f[i][S][k]+val(i+k,i+r)); r是枚举的下一个吃饭的人,k是上一个吃饭的人
然后我们可以观察这个状态转移方程,显然就这一个是不行的,i显然转移不过来,那我们考虑什么时候可以推向下一个状态
我们再来看状态的定义: 表示i之前的人都吃过了,i及i之后的人吃饭的状态是S,上一个吃饭的人与i的距离是k 的最小代价
那么显然如果i吃过饭了,就可以愉快的转移啦,即
if(S&1) f[i+1][S>>1][k-1]=min(f[i+1][S>>1][k-1],f[i][S][k])
还有一点细节就是我们再枚举i后面的人的时候,要保证合法,所以记录一个目前能到达距离的最小值,如果超过这个最小值就break;
if(i+r>dis)break;
dis=min(dis,i+r+b[i+r]);
然后就可以Accept了
*/
#include<bits/stdc++.h>
#define f(i,s,k) (g[i][s][k+8])
using namespace std;
int g[1010][300][20],a[1010],b[1010],T,n,inf;
int val(int x,int y){if(x==0)return 0;return a[x]^a[y];}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);
memset(g,10,sizeof(g));inf=g[0][0][0];f(1,0,-1)=0;
for(int i=1;i<=n;i++){
for(int s=0;s<(1<<8);s++){
for(int k=-8;k<=7;k++){
if(f(i,s,k)>=inf)continue;
if(s&1)f(i+1,s>>1,k-1)=min(f(i+1,s>>1,k-1),f(i,s,k));
else{
int dis=inf;
for(int r=0;r<=7;r++){
if((s&(1<<r))==0){
if(i+r>dis)break;
dis=min(dis,i+r+b[i+r]);
f(i,s|(1<<r),r)=min(f(i,s|(1<<r),r),f(i,s,k)+val(i+k,i+r));
}
}
}
}
}
}
int ans=inf;
for(int k=-8;k<=-1;k++)ans=min(ans,f(n+1,0,k));
cout<<ans<<"\n";
}
return 0;
}