http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1424
题意:给一面墙刷漆,要求刷下当前墙的时候,在其上面的墙必须都要已经刷完了。 求刷子换颜色的最少次数。
算法:状态DP。
状态转移方程:dp[i][j] = dp[k][j'] + 1。dp[i][j]表示在状态为j的情况下,最后一次刷的是i区域的最少转化次数。本题需要特别注意的就是需要考虑在当前状态下,最后一次刷i墙是否合法,即需要判断i墙上面的强是否都已经刷过了。其他的就和一般的状态DP一样了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N;
struct Node{
int uly,ulx,lry,lrx ;
int color ;
friend bool operator <(const Node& a,const Node& b)
{
if(a.uly == b.uly) return a.ulx < b.ulx ;
else return a.uly < b.uly ;
}
}block[16];
int dp[16][1<<15];
bool up(int k,int i) //判断k区域是否是在i区域的上方(即是否要求在刷i区域之前必须先刷完k区域)
{
if(block[k].lry<=block[i].uly) //注意:坐标原点是在左上角,因此在上面的要更小
{
if(block[k].ulx>=block[i].lrx || block[k].lrx<=block[i].ulx)
return false ;
else
return true;
}
else
return false ;
}
bool is_ok(int j,int i) //判断在j状态下最后一次刷的是i区域是否合法(这里是可以优化的,可以通过预处理优化);
{
for(int k=1;k<=N;k++)
{
if(k==i) continue ;
if(up(k,i))
{
if(0 == (j&(1<<(k-1))))
return false ;
}
}
return true;
}
int DP()
{
int new_s ;
for(int i=1;i<=N;i++)
{
for(int j=0;j<(1<<N);j++)
{
dp[i][j] = 20 ;
}
}
for(int j=0;j<(1<<N);j++)
{
for(int i=1;i<=N;i++)
{
new_s = (j&(1<<(i-1)));
if(new_s==0 || !is_ok(j,i)) continue ; //考虑当前墙i是最后刷的是否合法
new_s = ( j^(new_s) ) ;
if(new_s == 0)
{
dp[i][j] = 1; //相当于初始化
continue ;
}
int min_ = 20 ;
for(int k=1;k<=N;k++) //寻找前一状态
{
if(!is_ok(new_s,k)) continue ;
if(block[k].color == block[i].color)
{
min_ = min(min_,dp[k][new_s]);
}
else
min_ = min(min_,dp[k][new_s]+1) ;
}
dp[i][j] = min_ ;
}
}
int min_ = 20 ;
for(int i=1;i<=N;i++)
{
min_ = min(min_ ,dp[i][(1<<N)-1]) ;
}
return min_ ;
}
int main()
{
int T;
//freopen("1in.txt","r",stdin);
//freopen("1out.txt","w",stdout);
scanf("%d",&T);
while(T--)
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
scanf("%d %d %d %d %d",&block[i].uly,&block[i].ulx,&block[i].lry,&block[i].lrx,&block[i].color);
}
sort(block+1,block+N+1);
int ans = DP();
printf("%d\n",ans);
}
return 0;
}