DP নিয়ে আগের লিখাগুলা এই লিঙ্কে পাওয়া যাবে ।
এই পোস্টেও কিছু interesting bitmask dp problem নিয়ে লিখার ইচ্ছা আছে ।
Can You win ?
এইখানে বলা হইছে তুমি তোমার বোনের সাথে দাবা খেলতেছ কিন্তু খুব একটা পাত্তা পাইতেছ না ।তোমার শুধু মাত্র রাজা আর একটা ঘোড়া বাকি আছে । তোমার বোন তার প্রিয় " ‘panju muttai" খাইতে একটু বাহিরে গেছে , এই সুযোগ একটু দুই নাম্বারি করার । তুমি n টা মুভে যদি তোমার বোনের pawn ( 'p' small p , its important . this cause me 2/3 WA ) বাদে অন্য power ( capital 'P' ) খেয়ে ফেলতে পার তোমার ঘোড়া দিয়ে তুমি জিতবা । তোমারে দাবা বোর্ড এর কারেন্ট অবস্থা বলা হইছে তোমাকে বলতে হবে তুমি জিততে পারবা কি পারবা না ।
*** পাওয়ার এর সংখ্যা ১-৮ ( যখনই লিমিট কম থাকবে তখন আমাদের জন্য ভাল , আমরা একে bitmask এ ফালাইতে পারি না হয় state বাড়াইয়া দিতে পারি )
লিমিট কম তাই আমরা সহজেই bitmask করতে পারি । আমি 4টা state এ করছিলাম । কারণ x_position < 8 , y_position < 8 , maximum_move <= 64 , mask <= ( 1 << 8 ) । টোটাল ( 8 * 8 * 64 * ( 1 << 8 ) ) == 1048576 । প্রাথমিক ভাবে আমরা আমাদের ঘোড়ার পজিশন থেকে স্টার্ট করব । তারপর দেখব আমাদের যে মাক্সিমাম মুভ দেওয়া আছে এর মধ্যে আমরা সব গুলা পাওয়ারকে খেয়ে ফেলতে পারব কি না । যদি পারি তাইলে "Yes" না হবে "No" । এইখানে আমরা আমাদের কারেন্ট পজিশন থেকে যে সব পজিশনে পাওয়ার আছে তাতে কত মুভে যাওয়া যায় এইটা বের করার জন্য bfs() চালাব ।
Headmaster's Headache :
এই প্রবলেমটা অনেক ভাল কিন্তু বিরক্তিকর ইনপুট এর প্রবলেম । এই প্রবলেম এ বলা হইছে একজন হেড মাস্টার তার স্কুল এর জন্য কিছু শিক্ষক নিয়োগ এর আবেদন পেয়েছেন , যা থেকে তিনি কাউকে কাউকে নিয়োগ দিতে পারেন । স্কুল এ n টা সাবজেক্ট পড়ানো হয় । হেড স্যার চান যেন সব সাবজেক্ট এ কমপক্ষে দুইজন টিচার থাকেন , স্কুল এ কিছু পারমানেন্ট টিচার আছেন যাদের নিদিষ্ট বেতন দেওয়া হয় এবং তারা কিছু নিদিষ্ট সাবজেক্ট পড়ায় । এখন নতুন আবেদন করা শিক্ষকদের নিয়ে বা না নিয়ে হেড স্যার কিভাবে বিদ্যালয়ের খরচ ( মানে শিক্ষকদের বেতন দিতে পারেন , এইখানে বলে রাখা ভাল যারা পারমানেন্ট তারা অবশ্যই থাকবেন ) মিনিমাম করতে পারেন যেন অবশ্যই প্রতিটা সাবজেক্ট এ কমপক্ষে দুইজন টিচার থাকে ।
এইখানে সাবজেক্ট সর্বমোট থাকতে পারে ৮টা , লিমিট যেহেতু অনেক কম তাই আমরা এই প্রবলেমটাকে bitmask করতে পারি । এখন আমাদের state কয়েকটা হবে । যেহেতু পারমানেন্ট টিচার অবশ্যই থাকবে এইটা আমাদের বিবেচনার ব্যাপার না । আমাদের বিবেচনার ব্যাপার যারা আবেদন করেছেন । অবশ্যই একটা state হইল যারা আবেদন করেছেন তাদের সংখ্যা ( সর্বমোট ১০০ জন আবেদন করতে পারেন ) । এখন প্রতিটা সাবজেক্ট দুইজন করে টিচার পড়াবে যা বুঝার জন্য আমরা দুইটা mask রাখতে পারি । mask1 এর কোন পয়েন্ট ১ থাকা মানে ১ জন টিচার আছেন যা এ সাবজেক্ট টা পড়ান । mask2 এর কোন পয়েন্ট ১ থাকা মানে ২ জন টিচার আছেন যা ঐ সাবজেক্ট পড়ান । আমরা যখন দেখব ( mask1 == ( 1 << total_sum ) - 1 && mask2 == ( 1 << total_sub ) - 1 ) ) এই কন্ডিশন টা ট্রু হচ্ছে মানে আমরা সব সাবজেক্ট এ কমপক্ষে দুইজন টিচার আছে তা পেয়েগেছি । ইনপুট এর বিশাল প্রবলেম আছে এই প্রবলেম এ । এর বাদে আর কোন বিশেষ দিক নেই । straight forward bitmask dp এর প্রবলেম ।
এই পোস্টেও কিছু interesting bitmask dp problem নিয়ে লিখার ইচ্ছা আছে ।
Can You win ?
এইখানে বলা হইছে তুমি তোমার বোনের সাথে দাবা খেলতেছ কিন্তু খুব একটা পাত্তা পাইতেছ না ।তোমার শুধু মাত্র রাজা আর একটা ঘোড়া বাকি আছে । তোমার বোন তার প্রিয় " ‘panju muttai" খাইতে একটু বাহিরে গেছে , এই সুযোগ একটু দুই নাম্বারি করার । তুমি n টা মুভে যদি তোমার বোনের pawn ( 'p' small p , its important . this cause me 2/3 WA ) বাদে অন্য power ( capital 'P' ) খেয়ে ফেলতে পার তোমার ঘোড়া দিয়ে তুমি জিতবা । তোমারে দাবা বোর্ড এর কারেন্ট অবস্থা বলা হইছে তোমাকে বলতে হবে তুমি জিততে পারবা কি পারবা না ।
*** পাওয়ার এর সংখ্যা ১-৮ ( যখনই লিমিট কম থাকবে তখন আমাদের জন্য ভাল , আমরা একে bitmask এ ফালাইতে পারি না হয় state বাড়াইয়া দিতে পারি )
লিমিট কম তাই আমরা সহজেই bitmask করতে পারি । আমি 4টা state এ করছিলাম । কারণ x_position < 8 , y_position < 8 , maximum_move <= 64 , mask <= ( 1 << 8 ) । টোটাল ( 8 * 8 * 64 * ( 1 << 8 ) ) == 1048576 । প্রাথমিক ভাবে আমরা আমাদের ঘোড়ার পজিশন থেকে স্টার্ট করব । তারপর দেখব আমাদের যে মাক্সিমাম মুভ দেওয়া আছে এর মধ্যে আমরা সব গুলা পাওয়ারকে খেয়ে ফেলতে পারব কি না । যদি পারি তাইলে "Yes" না হবে "No" । এইখানে আমরা আমাদের কারেন্ট পজিশন থেকে যে সব পজিশনে পাওয়ার আছে তাতে কত মুভে যাওয়া যায় এইটা বের করার জন্য bfs() চালাব ।
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const int INF = 1 << 28 ; | |
int dx[]={2,1,-1,-2,-2,-1,1,2};int dy[]={1,2,2,1,-1,-2,-2,-1};//Knight Direction | |
int vis[ 8 ][ 8 ][ 64 ][ ( 1 << 8 ) + 1 ] , cs ; | |
bool dp[ 8 ][ 8 ][ 64 ][ ( 1 << 8 ) + 1 ] ; | |
int dis[ 8 ][ 8 ]; | |
char grid[ 8 ][ 8 ]; | |
pii pawn[ 10 ] , me ; | |
int tot ; | |
bool ok( int x , int y ) | |
{ | |
if( x < 0 || x >= 8 || y < 0 || y >= 8 ) return 0 ; | |
if( grid[x][y] == 'K' || grid[x][y] == 'p' ) return 0 ; | |
return 1 ; | |
} | |
int bfs( int sx , int sy , int ddx , int ddy ) | |
{ | |
queue < pii > q ; | |
queue < int > cost ; | |
q.push( mp( sx , sy ) ); | |
cost.push( 0 ); | |
int i , j ; | |
rep( i , 8 ) rep( j , 8 ) dis[i][j] = INF ; | |
dis[sx][sy] = 0 ; | |
while( !q.empty() ) | |
{ | |
int x = q.front().ff ; | |
int y = q.front().ss ; | |
int c = cost.front(); | |
cost.pop(); | |
q.pop(); | |
rep( i , 8 ) | |
{ | |
int nx = x + dx[i]; | |
int ny = y + dy[i]; | |
if( ok( nx , ny ) == 0 ) continue ; | |
if( nx == ddx && ddy == ny ) | |
{ | |
return c + 1 ; | |
} | |
if( grid[nx][ny] == 'P') continue ; | |
if( c + 1 < dis[nx][ny] ) | |
{ | |
dis[nx][ny] = c + 1 ; | |
q.push( mp( nx , ny ) ); | |
cost.push( c + 1 ); | |
} | |
} | |
} | |
return -1 ; | |
} | |
bool DP( int sx , int sy , int lft , int mask ) | |
{ | |
if( mask == ( 1 << tot ) - 1 ) return 1 ; | |
int &v = vis[sx][sy][lft][mask]; | |
bool &ret = dp[sx][sy][lft][mask]; | |
if( v == cs ) return ret ; | |
v = cs ; | |
ret = 0 ; | |
rep( i , tot ) | |
{ | |
if( mask & ( 1 << i ) ) continue ; | |
pii go = pawn[i]; | |
int d = bfs( sx , sy , go.ff , go.ss ); | |
if( d !=-1 && d <= lft ) | |
{ | |
grid[go.ff][go.ss] = '.'; | |
bool tmp = DP( go.ff , go.ss , lft - d , mask | ( 1 << i ) ); | |
if( tmp == 1 ) return ret = 1 ; | |
grid[ go.ff ][go.ss] = 'P'; | |
} | |
} | |
return ret ; | |
} | |
int main() | |
{ | |
int t = II ; | |
for ( cs = 1 ; cs <= t ; cs++ ) | |
{ | |
int n = II ; | |
rep( i , 8 ) scanf("%s",grid[i]); | |
rep( i , 8 ) rep( j , 8 ) | |
if( grid[i][j] == 'k' ) | |
{ | |
me = mp( i , j ); | |
break ; | |
} | |
tot = 0 ; | |
rep( i , 8 ) rep( j , 8 ) | |
{ | |
if( grid[i][j] == 'P') | |
{ | |
pawn[tot].ff = i ; | |
pawn[tot++].ss = j ; | |
} | |
} | |
if( DP( me.ff , me.ss , n , 0 ) ) printf("Yes\n"); | |
else printf("No\n"); | |
} | |
return 0; | |
} |
Headmaster's Headache :
এই প্রবলেমটা অনেক ভাল কিন্তু বিরক্তিকর ইনপুট এর প্রবলেম । এই প্রবলেম এ বলা হইছে একজন হেড মাস্টার তার স্কুল এর জন্য কিছু শিক্ষক নিয়োগ এর আবেদন পেয়েছেন , যা থেকে তিনি কাউকে কাউকে নিয়োগ দিতে পারেন । স্কুল এ n টা সাবজেক্ট পড়ানো হয় । হেড স্যার চান যেন সব সাবজেক্ট এ কমপক্ষে দুইজন টিচার থাকেন , স্কুল এ কিছু পারমানেন্ট টিচার আছেন যাদের নিদিষ্ট বেতন দেওয়া হয় এবং তারা কিছু নিদিষ্ট সাবজেক্ট পড়ায় । এখন নতুন আবেদন করা শিক্ষকদের নিয়ে বা না নিয়ে হেড স্যার কিভাবে বিদ্যালয়ের খরচ ( মানে শিক্ষকদের বেতন দিতে পারেন , এইখানে বলে রাখা ভাল যারা পারমানেন্ট তারা অবশ্যই থাকবেন ) মিনিমাম করতে পারেন যেন অবশ্যই প্রতিটা সাবজেক্ট এ কমপক্ষে দুইজন টিচার থাকে ।
এইখানে সাবজেক্ট সর্বমোট থাকতে পারে ৮টা , লিমিট যেহেতু অনেক কম তাই আমরা এই প্রবলেমটাকে bitmask করতে পারি । এখন আমাদের state কয়েকটা হবে । যেহেতু পারমানেন্ট টিচার অবশ্যই থাকবে এইটা আমাদের বিবেচনার ব্যাপার না । আমাদের বিবেচনার ব্যাপার যারা আবেদন করেছেন । অবশ্যই একটা state হইল যারা আবেদন করেছেন তাদের সংখ্যা ( সর্বমোট ১০০ জন আবেদন করতে পারেন ) । এখন প্রতিটা সাবজেক্ট দুইজন করে টিচার পড়াবে যা বুঝার জন্য আমরা দুইটা mask রাখতে পারি । mask1 এর কোন পয়েন্ট ১ থাকা মানে ১ জন টিচার আছেন যা এ সাবজেক্ট টা পড়ান । mask2 এর কোন পয়েন্ট ১ থাকা মানে ২ জন টিচার আছেন যা ঐ সাবজেক্ট পড়ান । আমরা যখন দেখব ( mask1 == ( 1 << total_sum ) - 1 && mask2 == ( 1 << total_sub ) - 1 ) ) এই কন্ডিশন টা ট্রু হচ্ছে মানে আমরা সব সাবজেক্ট এ কমপক্ষে দুইজন টিচার আছে তা পেয়েগেছি । ইনপুট এর বিশাল প্রবলেম আছে এই প্রবলেম এ । এর বাদে আর কোন বিশেষ দিক নেই । straight forward bitmask dp এর প্রবলেম ।
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const int NX = 104 ; | |
const int INF = 1 << 29 ; | |
const int MX = ( 1 << 8 ) + 3 ; | |
int vis[ NX ][ MX ][ MX ] , cs , dp[ NX ][ MX ][ MX ]; | |
int sv[ NX ] , n , m , lim , need[ NX ] ; | |
int DP( int pos , int mask1 , int mask2 ) | |
{ | |
if( mask1 == lim && mask2 == lim ) return 0 ; | |
if( pos == n ) return INF ; | |
int &v = vis[pos][mask1][mask2]; | |
int &ret = dp[pos][mask1][mask2]; | |
if( v == cs ) return ret ; | |
v = cs ; | |
ret = INF ; | |
int tmp = ( mask1 & sv[pos] ); | |
ret = min( ret , DP ( pos + 1 , mask1 | sv[pos] , mask2 | tmp ) + need[pos] ); | |
ret = min( ret , DP( pos + 1 , mask1 , mask2 ) ); | |
return ret ; | |
} | |
int main() | |
{ | |
// I will always use scanf and printf | |
while( scanf("%d %d %d",&lim,&m,&n ) == 3 ) | |
{ | |
cs++; | |
getchar(); | |
if( lim == 0 && n == 0 && m == 0 ) break ; | |
lim = ( 1 << lim ) - 1 ; | |
int mask1 = 0 , mask2 = 0 ; | |
int ans = 0 ; | |
string inp ; | |
stringstream ss ; | |
rep( i , m ) | |
{ | |
ss.clear(); | |
getline( cin , inp ); | |
ss << inp ; | |
int fst = 1 , val ; | |
int mask = 0 ; | |
while( ss >> val ) | |
{ | |
if( fst ) | |
{ | |
ans += val ; | |
fst = 0 ; | |
} | |
else | |
{ | |
mask |= ( 1 << ( val - 1 ) ); | |
} | |
} | |
int tmp = ( mask1 & mask ); | |
mask1 |= ( mask ); | |
mask2 |= tmp ; | |
} | |
rep( i , n ) | |
{ | |
ss.clear(); // clear is important | |
getline( cin , inp ); | |
ss << inp ; | |
int fst = 1 , val ; | |
sv[i] = 0 ; | |
while( ss >> val ) | |
{ | |
if( fst ) | |
{ | |
need[i] = val ; | |
fst = 0 ; | |
} | |
else | |
{ | |
sv[i] |= ( 1 << (val - 1 )); | |
} | |
} | |
} | |
printf("%d\n",DP( 0 , mask1 , mask2 ) + ans ); | |
} | |
return 0; | |
} |
Anagram Division
এই প্রবলেম এ আমাদের বলা হইছে আমাদের একটা string ( s ) আর একটা নাম্বার ( d ) দেওয়া হবে । আমাদের বলতে হবে স্ট্রিংটা দ্বারা কয়টা নাম্বার এরকম করা যায় যা আমাদের এই নাম্বার d দ্বারা ভাগ যাবে ।
এই প্রবলেমটা ততক্ষণ পর্যন্ত কঠিন লাগতে পারে যতক্ষণ পর্যন্ত আমরা bitmask dp এর কথা ভাবি না । এইখানে বলা আছে স্ট্রিং এর লেন্থ হবে হাইস্ট ১০ ( যা খুব ভাল bitmask এর জন্য ) এবং d এর মান হাইস্ট হবে < ১০০১ যাও অনেক ভাল । আমরা চাইলেই dp[ ( 1 << 10 ) ][ 1005 ] খুব সহজেই declear করতে পারি । এইখানে একটা উল্লেখ্য ব্যাপার আছে । যেহেতু permutation জড়িত , একই position main initial string এর different position same digit বসিয়েও আমরা একই নাম্বার তৈরি করতে পারি যা হবে ডুপলিকেট । ধরি আমাদের দেওয়া হল ০০০ স্ট্রিং । এইখানে bitmasking এ আমরা শুধু মাত্র position check করি । first index এ তার initial index এর 1 , 2 , 3 rd সব পজিশনের ভ্যালু বসেই কল হয় । কিন্তু আমাদের এই প্রবলেম এর জন্য আমাদের এই চেকটাও রাখতে হবে , আমরা যদি first index এ initial index এর 1 st position এর 0 বসাই তাহলে বসতে পারে initial string থেকে 2 , 3rd position এর ০ আমরা ১ নাম্বার পজিশনে বাসাব না । কারণ বসালে একই জিনিস দুই/আরও বেশিও হতে পারে বসবে যা ডুপলিকেট অনেক কিছু তৈরি করবে । এর জন্য আমরা লোকাল ফ্লাগ array ( size 10 is good ) ব্যাবহার করব ।
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
string inp ; | |
int lim ; | |
int dp[ ( 1 << 10 ) + 1 ][ 1005 ] , d ; | |
int vis[ ( 1 << 10 ) + 1 ][ 1005 ] , cs ; | |
int DP( int mask , int mod ) | |
{ | |
if( mask == ( 1 << lim ) - 1 ) | |
{ | |
return mod == 0 ; // if mod is still 0 that means this generated number can be divisible by d | |
} | |
int &ret = dp[mask][mod]; | |
int &v = vis[ mask ][mod ]; | |
if( v == cs ) return ret ; | |
v = cs ; | |
int flag[10] = {0}; | |
int i ; | |
ret = 0 ; | |
for ( i = 0 ; i < lim ; i++ ) | |
{ | |
int x = inp[i] - '0' ; | |
if( mask & ( 1 << i ) || flag[x] ) continue ; | |
flag[x] = 1 ; // make sure digit x wont place this position again | |
ret += DP( mask | ( 1 << i ) , ( ( mod * 10 ) + x )%d ); | |
} | |
return ret ; | |
} | |
int main() | |
{ | |
// I will always use scanf and printf | |
// May be i won't be a good programmer but i will be a good human being | |
int t = II ; | |
for ( cs = 1 ; cs <= t ; cs++ ) | |
{ | |
cin >> inp ; | |
d = II ; | |
lim = inp.size(); | |
int ans = DP( 0 , 0 ); | |
printf("Case %d: %d\n",cs,ans); | |
} | |
return 0; | |
} |
It is good article for beginners.thank you
ReplyDeleteweb programming tutorial
welookups