简单题
1.学校食堂
看起来很简单,结果写了好久好久。我太弱啦。
dp[i][j][k]表示i以前的都处理好了,j表示 i及i以后的七个人的01处理状态,k表示这个状态是由哪个人转移过来的(1~mm表示i之前的,mm+1以后表示i和i之后的人)
一开始感觉很好转移,写起来就出现了很多问题,根本不知道如何预处理?
后来问了杯哥,发现自己智障地以为i的状态就该向i以后的状态转移,其实i是可以向自己转移的QAQ
两种转移,一是j未到达全1,向i自己转移,二是j的前b项达到全1就向i+b转移。
自己写丑了,感觉别人的代码都贼短,不过懒得又抄一遍题解啦,反正自己瞎YY的丑就丑吧
//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int M=1000+5;
const int N=(1<<9);
int T,n,nn,mm,ti[M],rn[M],dp[M][N][17],no[M][N],cnt[N];
void init(){
scanf("%d",&n);
mm=0;
for(int i=1;i<=n;i++){
scanf("%d%d",&ti[i],&rn[i]);
mm=max(mm,rn[i]);
}
mm++;
}
void pre(){
nn=(1<<mm)-1;
for(int i=1;i<=n;i++)
for(int j=0;j<=nn;j++){
int rr=min(i+mm-1,n),cc=0,xx;
for(int k=0;k<mm;k++){
if(j&(1<<k)){
if(i+k>rr) no[i][j]=1;
cc++;
xx=k+1;
}
else rr=min(rr,i+k+rn[i+k]);
}
cnt[j]=cc;
if(cnt[j]==1)
dp[1][j][xx+mm]=0;
}
}
void work(){
for(int i=1;i<=n;i++) {
for(int k=0;k<=mm;k++) {
for(int j=0;j<=nn;j++) {
if(no[i][j]||cnt[j]!=k) continue;
for(int l=1;l<=(mm+1)*2;l++) {
if(dp[i][j][l]==-1) continue;
for(int s=0;s<mm;s++) {
if(!(j&(1<<s))) {
int now=j|(1<<s);
if(no[i][now]) continue;
int pr=(l<mm+1)?i-(mm+1-l):i+l-(mm+1);
int cost=(ti[pr]|ti[i+s])-(ti[pr]&ti[i+s]);
if(dp[i][now][mm+1+s]==-1||dp[i][now][mm+1+s]>dp[i][j][l]+cost)
dp[i][now][mm+1+s]=dp[i][j][l]+cost;
}
}
for(int s=0;s<mm;s++) {
if(!(j&(1<<s))) break;
int now=j>>s+1;
if(dp[i+s+1][now][l-s-1]==-1||dp[i+s+1][now][l-s-1]>dp[i][j][l])
dp[i+s+1][now][l-s-1]=dp[i][j][l];
}
}
}
}
}
int ans=1000000000;
for(int i=1;i<=(mm+1)*2;i++){
if(dp[n][1][i]!=-1) ans=min(ans,dp[n][1][i]);
}
printf("%d
",ans);
}
void clear(){
memset(dp,-1,sizeof(dp));
memset(no,0,sizeof(no));
}
int main()
{
scanf("%d",&T);
while(T--){
clear();
init();
pre();
work();
}
return 0;
}
2.炮兵布阵
先预处理出所有不合法的状态,有冲突的和每一行是1的位置为山地的为不合法。
dp[i][j][k]表示处理到了第i行,它的状态为j,它上一行的状态为k的最优解,转移的时候再判断一下三行直接冲不冲突即可。
//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn=(1<<10);
int ans,nn,dp[105][61][61],a[105][20],n,m,zt[65],zts,gs[65],no[105][61];
char ch[20];
void pre(){
nn=((1<<m)-1);
for(int i=0;i<=nn;i++){
int pre=-3,tp=i,k=0,v=0;
while(tp){
k++;
if(tp&1) {
v++;
if(k-pre<3) break;
pre=k;
}
tp>>=1;
}
if(!tp) {
zt[++zts]=i;
gs[zts]=v;
}
}
for(int i=1;i<=zts;i++) dp[1][1][i]=gs[i];
for(int i=1;i<=zts;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=n;k++)
if((zt[i]&(1<<(j-1)))&&a[k][j]) no[k][i]=1;
}
int check(int x,int y){
for(int i=1;i<=m;i++) {
if((zt[x]&(1<<(i-1)))&&((zt[y]&(1<<(i-1))))) return 0;
}
return 1;
}
int ok(int j,int k,int l){
if(!check(j,k)) return 0;
if(!check(k,l)) return 0;
if(!check(j,l)) return 0;
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",ch);
for(int j=0;j<m;j++)
if(ch[j]=='H')
a[i][j+1]=1;
}
pre();
for(int i=2;i<=n;i++)
for(int j=1;j<=zts;j++) { //上二行
if((i==2&&j!=1)||(no[i-2][j])) continue;
for(int k=1;k<=zts;k++) { //上一行
if(no[i-1][k]) continue;
for(int l=1;l<=zts;l++) { //当前行
if(no[i][l]||!ok(j,k,l)) continue;
dp[i][k][l]=max(dp[i][k][l],dp[i-1][j][k]+gs[l]);
}
}
}
for(int i=1;i<=zts;i++)
for(int j=1;j<=zts;j++)
ans=max(ans,dp[n][i][j]);
printf("%d
",ans);
return 0;
}
3.NOIP2016愤怒的小鸟
很久之前抄的题解,忘得差不多了,先码着
//Twenty
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
double eps=1e-10;
int t,n,m,f[20][20],cnt,dp[1<<20];
double x[20],y[20];
int min(int a,int b){
return a<b?a:b;
}
void makebag()
{
memset(f,0,sizeof(f));
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
{
double x1=x[i],x2=x[j],y1=y[i],y2=y[j];
double a,b;
a=(y1*x2-y2*x1)/(x1*x2*(x1-x2));
b=((y1*x2*x2-y2*x1*x1)/(x1*x2*x2-x1*x1*x2));
if(a>=-eps) continue;
for(int k=i;k<n;k++)
{
if(fabs(a*x[k]*x[k]+b*x[k]-y[k])<=eps)
f[i][j]|=(1<<k);
}
}
}
int dpp()
{
memset(dp,127/3,sizeof(dp));
dp[0]=0;int i;
for(int v=0;v<(1<<n)-1;v++)
{
i=0;
while(v&(1<<i)) i++;
dp[v|(1<<i)]=min(dp[v|(1<<i)],dp[v]+1);
for(int j=i;j<n;j++)
dp[v|f[i][j]]=min(dp[v|f[i][j]],dp[v]+1);
}
}
int main()
{
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%lf%lf",&x[i],&y[i]);
makebag();
dpp();
printf("%d
",dp[(1<<n)-1]);
}
return 0;
}
4.坑人水题 bzoj3446 Cow Decathlon
题目很水。不过第一次知道了预处理出状态中1的个数来优化转移。
有个坑点是每个时间段可能有多个特别奖励的条件,调了好久。。
//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn=1<<20;
int n,b,cntsp,sp[21][2],v[21][21],x,y,z,nn,dp[21][maxn],bit[maxn];
vector<int>s[22];
int main()
{
scanf("%d%d",&n,&b);
for(int i=1;i<=b;i++){
scanf("%d%d%d",&x,&y,&z);
sp[++cntsp][0]=y;
sp[cntsp][1]=z;
s[x].push_back(cntsp);
}
nn=(1<<n)-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&v[i][j]);
for(int i=0;i<=nn;i++){
int base=0;
for(int j=1;j<=n;j++){
if(i&(1<<(j-1))) base++;
}
bit[i]=base;
}
for(int i=1;i<=n;i++)
for(int j=0;j<=nn;j++){
if(bit[j]!=i) continue;
for(int k=1;k<=n;k++)
{
if(j&((1<<(k-1))))
{
int now=v[k][i];
for(int kd=0;kd<s[i].size();kd++){
int xx=sp[s[i][kd]][1],yy=sp[s[i][kd]][0];
if(now+dp[i-1][j^(1<<(k-1))]>=yy)
now+=xx;
}//坑爹啊 一个点可能有多个特殊价值
dp[i][j]=max(dp[i][j],dp[i-1][j^(1<<(k-1))]+now);
}
}
}
printf("%d
",dp[n][nn]);
return 0;
}
5.水题 扫雷
//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn=10000+299;
int ans,n,a[maxn],dp[maxn][8],cnt[10];
void pre(){
for(int i=0;i<8;i++)
for(int j=0;j<3;j++)
if(i&(1<<j)) cnt[i]++;
dp[0][0]=dp[0][1]=1;
}
void init(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
}
void work(){
for(int i=1;i<=n;i++) {
for(int j=0;j<8;j++) {
if(cnt[j]!=a[i]||(i==1&&(j&4))||(i==n&&(j&1))) continue;
dp[i][j]+=(dp[i-1][j>>1]+dp[i-1][(j>>1)^4]);
if(i==n) ans+=dp[i][j];
}
}
printf("%d
",ans);
}
int main()
{
pre();
init();
work();
return 0;
}
6.vijos 兴奋剂检查
第一次见这样的题,又抄了题解,算是知道了状态压缩的含义,但对题目给的数据范围和这个压缩方式表示怀疑。
int has(int a,int b,int c,int d,int e){
return a*(v[2]+1)*(v[3]+1)*(v[4]+1)*(v[5]+1)+b*(v[3]+1)*(v[4]+1)*(v[5]+1)+c*(v[4]+1)*(v[5]+1)+d*(v[5]+1)+e;
}
//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn=5000000+299;
int ans,n,m,dp[maxn],v[6],val,q[6];
int has(int a,int b,int c,int d,int e){
return a*(v[2]+1)*(v[3]+1)*(v[4]+1)*(v[5]+1)+b*(v[3]+1)*(v[4]+1)*(v[5]+1)+c*(v[4]+1)*(v[5]+1)+d*(v[5]+1)+e;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d",&v[i]);
for(int i=1;i<=n;i++){
scanf("%d",&val);
for(int i=1;i<=m;i++) scanf("%d",&q[i]);
for(int a=v[1];a>=q[1];a--)
for(int b=v[2];b>=q[2];b--)
for(int c=v[3];c>=q[3];c--)
for(int d=v[4];d>=q[4];d--)
for(int e=v[5];e>=q[5];e--){
int x=has(a,b,c,d,e),y=has(a-q[1],b-q[2],c-q[3],d-q[4],e-q[5]);
dp[x]=max(dp[x],dp[y]+val);
ans=max(ans,dp[x]);
}
}
printf("%d
",ans);
return 0;
}
7.水题 vijos 严厉的班长
一开始脑残了认为一个非1的数跟它自己互质,喵喵喵?
然后不是的话就很好做了,我们筛出100以内的质数,发现最大会有16个,因为大于59的没有选1划算。
知道了这一点就可以直接dp了
//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn=1<<16;
int n,prime[18]={0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
int a[maxn],dp[101][maxn],zx[60],nn,ans=1e9+7;
void init(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
}
void pre(){
for(int i=2;i<=58;i++) {
int tp=i;
for(int j=1;j<=16;j++) {
if(tp==1) break;
if(tp%prime[j]==0){
zx[i]=zx[i]|(1<<(j-1));
while(tp%prime[j]==0) tp/=prime[j];
}
}
}
}
void work() {
memset(dp,127,sizeof(dp));
dp[0][0]=0;
for(int i=0;i<n;i++)
for(int j=1;j<=58;j++)
for(int k=0;k<maxn;k++) {
if(k&zx[j]) continue;
dp[i+1][k|zx[j]]=min(dp[i+1][k|zx[j]],dp[i][k]+abs(a[i+1]-j));
if(i+1==n) ans=min(ans,dp[i+1][k|zx[j]]);
}
printf("%d
",ans);
}
int main()
{
init();
pre();
work();
return 0;
}
8.水题 最小总代价
感觉没什么存在意义的水题。大概就是体现一下水是生命之源吧。
顺便vijos上的难度真是一点参考价值没有。
//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn=1<<16;
int ans=1e9,n,nn,pr,v[20][20],cnt[maxn],dp[maxn][20];
void pre(){
nn=(1<<n)-1;
for(int i=1;i<=nn;i++){
cnt[i]=0; pr=0;
for(int j=0;j<16;j++)
if(i&(1<<j)) {cnt[i]++; pr=j+1;}
if(cnt[i]==1) dp[i][pr]=0;
}
}
int main()
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
memset(dp,127,sizeof(dp));
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&v[i][j]);
pre();
for(int i=1;i<=n;i++)
for(int j=1;j<=nn;j++){
if(cnt[j]!=i) continue;
for(int k=1;k<=n;k++){
if(dp[j][k]==-1) continue;
for(int l=1;l<=n;l++){
if(j&(1<<l-1)) continue;
dp[j|(1<<l-1)][l]=min(dp[j|(1<<l-1)][l],dp[j][k]+v[k][l]);
}
}
}
for(int i=1;i<=n;i++)
ans=min(ans,dp[nn][i]);
printf("%d
",ans);
return 0;
}
9.洛谷 2915 [USACO08NOV]奶牛混合起来Mixed Up Cows
水题
10.BZOJ 1087 [SCOI2005]互不侵犯King
水题
中等题
状压+计算几何
轮廓线dp
奥妙重重的状压dp。
较难题
可以模拟退火乱搞